Home | History | Annotate | Download | only in jdk8
      1 /**
      2  * Copyright (C) 2014 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.jdk8;
     18 
     19 import static java.lang.annotation.ElementType.METHOD;
     20 import static java.lang.annotation.ElementType.TYPE;
     21 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     22 
     23 import com.google.inject.AbstractModule;
     24 import com.google.inject.Guice;
     25 import com.google.inject.Injector;
     26 import com.google.inject.matcher.Matchers;
     27 
     28 import junit.framework.TestCase;
     29 
     30 import org.aopalliance.intercept.MethodInterceptor;
     31 
     32 import java.lang.annotation.Retention;
     33 import java.lang.annotation.Target;
     34 import java.util.concurrent.atomic.AtomicInteger;
     35 
     36 /**
     37  * Tests for interception of default methods.
     38  *
     39  * @author cgdecker (at) google.com (Colin Decker)
     40  */
     41 public class DefaultMethodInterceptionTest extends TestCase {
     42 
     43   private static final AtomicInteger callCount = new AtomicInteger(0);
     44   private static final AtomicInteger interceptedCallCount = new AtomicInteger(0);
     45 
     46   // the interceptor's a lambda too
     47   private final MethodInterceptor interceptor = invocation -> {
     48     interceptedCallCount.incrementAndGet();
     49     return invocation.proceed();
     50   };
     51 
     52   @Override
     53   protected void setUp() throws Exception {
     54     callCount.set(0);
     55     interceptedCallCount.set(0);
     56   }
     57 
     58   @Retention(RUNTIME)
     59   @Target({METHOD, TYPE})
     60   public @interface InterceptMe {}
     61 
     62   /** Interface with a default method annotated to be intercepted. */
     63   public interface Foo {
     64     @InterceptMe
     65     default String defaultMethod() {
     66       callCount.incrementAndGet();
     67       return "Foo";
     68     }
     69   }
     70 
     71   /** Foo implementation that does not override the default method. */
     72   public static class NonOverridingFoo implements Foo {
     73     public String methodCallingDefault() {
     74       return "NonOverriding-" + defaultMethod();
     75     }
     76   }
     77 
     78   public void testInterceptedDefaultMethod() {
     79     Injector injector = Guice.createInjector(new AbstractModule() {
     80       @Override
     81       protected void configure() {
     82         bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
     83             interceptor);
     84         bind(Foo.class).to(NonOverridingFoo.class);
     85       }
     86     });
     87 
     88     Foo foo = injector.getInstance(Foo.class);
     89     assertEquals("Foo", foo.defaultMethod());
     90     assertEquals(1, callCount.get());
     91     assertEquals(1, interceptedCallCount.get());
     92   }
     93 
     94   public void testInterceptedDefaultMethod_calledByAnotherMethod() {
     95     Injector injector = Guice.createInjector(new AbstractModule() {
     96       @Override
     97       protected void configure() {
     98         bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
     99             interceptor);
    100       }
    101     });
    102 
    103     NonOverridingFoo foo = injector.getInstance(NonOverridingFoo.class);
    104     assertEquals("NonOverriding-Foo", foo.methodCallingDefault());
    105     assertEquals(1, callCount.get());
    106     assertEquals(1, interceptedCallCount.get());
    107   }
    108 
    109   /** A base class defining a method with the same signature as Foo's default method. */
    110   public static class BaseClass {
    111     // the definition of this method on the class will win over the default method
    112     public String defaultMethod() {
    113       callCount.incrementAndGet();
    114       return "BaseClass";
    115     }
    116   }
    117 
    118   /** Foo implementation that should use superclass method rather than default method. */
    119   public static class InheritingFoo extends BaseClass implements Foo {
    120   }
    121 
    122   public void testInterceptedDefaultMethod_whenParentClassDefinesNonInterceptedMethod() {
    123     Injector injector = Guice.createInjector(new AbstractModule() {
    124       @Override
    125       protected void configure() {
    126         bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
    127             interceptor);
    128         bind(Foo.class).to(InheritingFoo.class);
    129       }
    130     });
    131 
    132     // the concrete implementation that wins is not annotated
    133     Foo foo = injector.getInstance(Foo.class);
    134     assertEquals("BaseClass", foo.defaultMethod());
    135     assertEquals(1, callCount.get());
    136     assertEquals(0, interceptedCallCount.get());
    137   }
    138 
    139   /**
    140    * A base class defining an intercepted method with the same signature as Foo's default method.
    141    */
    142   public static class BaseClass2 {
    143     // the definition of this method on the class will win over the default method
    144     @InterceptMe
    145     public String defaultMethod() {
    146       callCount.incrementAndGet();
    147       return "BaseClass2";
    148     }
    149   }
    150 
    151   /**
    152    * Foo implementation that should use intercepted superclass method rather than default method.
    153    */
    154   public static class InheritingFoo2 extends BaseClass2 implements Foo {
    155   }
    156 
    157   public void testInterceptedDefaultMethod_whenParentClassDefinesInterceptedMethod() {
    158     Injector injector = Guice.createInjector(new AbstractModule() {
    159       @Override
    160       protected void configure() {
    161         bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
    162             interceptor);
    163         bind(Foo.class).to(InheritingFoo2.class);
    164       }
    165     });
    166 
    167     // the concrete implementation that wins is not annotated
    168     Foo foo = injector.getInstance(Foo.class);
    169     assertEquals("BaseClass2", foo.defaultMethod());
    170     assertEquals(1, callCount.get());
    171     assertEquals(1, interceptedCallCount.get());
    172   }
    173 
    174   public interface Baz {
    175     default String doSomething() {
    176       return "Baz";
    177     }
    178 
    179     String doSomethingElse();
    180   }
    181 
    182   public static class BazImpl implements Baz {
    183 
    184     @Override
    185     public String doSomethingElse() {
    186       return "BazImpl";
    187     }
    188   }
    189 
    190   public void testInterception_ofAllMethodsOnType() {
    191     Injector injector = Guice.createInjector(new AbstractModule() {
    192       @Override
    193       protected void configure() {
    194         bindInterceptor(Matchers.subclassesOf(Baz.class), Matchers.any(), interceptor);
    195         bind(Baz.class).to(BazImpl.class);
    196       }
    197     });
    198 
    199     Baz baz = injector.getInstance(Baz.class);
    200 
    201     assertEquals("Baz", baz.doSomething());
    202     assertEquals("BazImpl", baz.doSomethingElse());
    203 
    204     assertEquals(2, interceptedCallCount.get());
    205   }
    206 
    207   public void testInterception_ofAllMethodsOnType_interceptsInheritedDefaultMethod() {
    208     Injector injector = Guice.createInjector(new AbstractModule() {
    209       @Override
    210       protected void configure() {
    211         bindInterceptor(Matchers.subclassesOf(BazImpl.class), Matchers.any(), interceptor);
    212         bind(Baz.class).to(BazImpl.class);
    213       }
    214     });
    215 
    216     Baz baz = injector.getInstance(Baz.class);
    217 
    218     assertEquals("Baz", baz.doSomething());
    219     assertEquals("BazImpl", baz.doSomethingElse());
    220 
    221     assertEquals(2, interceptedCallCount.get());
    222   }
    223 }
    224