• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import java.lang.annotation.Retention;
28 import java.lang.annotation.Target;
29 import java.util.concurrent.atomic.AtomicInteger;
30 import junit.framework.TestCase;
31 import org.aopalliance.intercept.MethodInterceptor;
32 
33 /**
34  * Tests for interception of default methods.
35  *
36  * @author cgdecker@google.com (Colin Decker)
37  */
38 public class DefaultMethodInterceptionTest extends TestCase {
39 
40   private static final AtomicInteger callCount = new AtomicInteger(0);
41   private static final AtomicInteger interceptedCallCount = new AtomicInteger(0);
42 
43   // the interceptor's a lambda too
44   private final MethodInterceptor interceptor =
45       invocation -> {
46         interceptedCallCount.incrementAndGet();
47         return invocation.proceed();
48       };
49 
50   @Override
setUp()51   protected void setUp() throws Exception {
52     callCount.set(0);
53     interceptedCallCount.set(0);
54   }
55 
56   @Retention(RUNTIME)
57   @Target({METHOD, TYPE})
58   public @interface InterceptMe {}
59 
60   /** Interface with a default method annotated to be intercepted. */
61   public interface Foo {
62     @InterceptMe
defaultMethod()63     default String defaultMethod() {
64       callCount.incrementAndGet();
65       return "Foo";
66     }
67   }
68 
69   /** Foo implementation that does not override the default method. */
70   public static class NonOverridingFoo implements Foo {
methodCallingDefault()71     public String methodCallingDefault() {
72       return "NonOverriding-" + defaultMethod();
73     }
74   }
75 
testInterceptedDefaultMethod()76   public void testInterceptedDefaultMethod() {
77     Injector injector =
78         Guice.createInjector(
79             new AbstractModule() {
80               @Override
81               protected void configure() {
82                 bindInterceptor(
83                     Matchers.any(), Matchers.annotatedWith(InterceptMe.class), 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 
testInterceptedDefaultMethod_calledByAnotherMethod()94   public void testInterceptedDefaultMethod_calledByAnotherMethod() {
95     Injector injector =
96         Guice.createInjector(
97             new AbstractModule() {
98               @Override
99               protected void configure() {
100                 bindInterceptor(
101                     Matchers.any(), Matchers.annotatedWith(InterceptMe.class), interceptor);
102               }
103             });
104 
105     NonOverridingFoo foo = injector.getInstance(NonOverridingFoo.class);
106     assertEquals("NonOverriding-Foo", foo.methodCallingDefault());
107     assertEquals(1, callCount.get());
108     assertEquals(1, interceptedCallCount.get());
109   }
110 
111   /** A base class defining a method with the same signature as Foo's default method. */
112   public static class BaseClass {
113     // the definition of this method on the class will win over the default method
defaultMethod()114     public String defaultMethod() {
115       callCount.incrementAndGet();
116       return "BaseClass";
117     }
118   }
119 
120   /** Foo implementation that should use superclass method rather than default method. */
121   public static class InheritingFoo extends BaseClass implements Foo {}
122 
testInterceptedDefaultMethod_whenParentClassDefinesNonInterceptedMethod()123   public void testInterceptedDefaultMethod_whenParentClassDefinesNonInterceptedMethod() {
124     Injector injector =
125         Guice.createInjector(
126             new AbstractModule() {
127               @Override
128               protected void configure() {
129                 bindInterceptor(
130                     Matchers.any(), Matchers.annotatedWith(InterceptMe.class), interceptor);
131                 bind(Foo.class).to(InheritingFoo.class);
132               }
133             });
134 
135     // the concrete implementation that wins is not annotated
136     Foo foo = injector.getInstance(Foo.class);
137     assertEquals("BaseClass", foo.defaultMethod());
138     assertEquals(1, callCount.get());
139     assertEquals(0, interceptedCallCount.get());
140   }
141 
142   /**
143    * A base class defining an intercepted method with the same signature as Foo's default method.
144    */
145   public static class BaseClass2 {
146     // the definition of this method on the class will win over the default method
147     @InterceptMe
defaultMethod()148     public String defaultMethod() {
149       callCount.incrementAndGet();
150       return "BaseClass2";
151     }
152   }
153 
154   /**
155    * Foo implementation that should use intercepted superclass method rather than default method.
156    */
157   public static class InheritingFoo2 extends BaseClass2 implements Foo {}
158 
testInterceptedDefaultMethod_whenParentClassDefinesInterceptedMethod()159   public void testInterceptedDefaultMethod_whenParentClassDefinesInterceptedMethod() {
160     Injector injector =
161         Guice.createInjector(
162             new AbstractModule() {
163               @Override
164               protected void configure() {
165                 bindInterceptor(
166                     Matchers.any(), Matchers.annotatedWith(InterceptMe.class), interceptor);
167                 bind(Foo.class).to(InheritingFoo2.class);
168               }
169             });
170 
171     // the concrete implementation that wins is not annotated
172     Foo foo = injector.getInstance(Foo.class);
173     assertEquals("BaseClass2", foo.defaultMethod());
174     assertEquals(1, callCount.get());
175     assertEquals(1, interceptedCallCount.get());
176   }
177 
178   public interface Baz {
doSomething()179     default String doSomething() {
180       return "Baz";
181     }
182 
doSomethingElse()183     String doSomethingElse();
184   }
185 
186   public static class BazImpl implements Baz {
187 
188     @Override
doSomethingElse()189     public String doSomethingElse() {
190       return "BazImpl";
191     }
192   }
193 
testInterception_ofAllMethodsOnType()194   public void testInterception_ofAllMethodsOnType() {
195     Injector injector =
196         Guice.createInjector(
197             new AbstractModule() {
198               @Override
199               protected void configure() {
200                 bindInterceptor(Matchers.subclassesOf(Baz.class), Matchers.any(), interceptor);
201                 bind(Baz.class).to(BazImpl.class);
202               }
203             });
204 
205     Baz baz = injector.getInstance(Baz.class);
206 
207     assertEquals("Baz", baz.doSomething());
208     assertEquals("BazImpl", baz.doSomethingElse());
209 
210     assertEquals(2, interceptedCallCount.get());
211   }
212 
testInterception_ofAllMethodsOnType_interceptsInheritedDefaultMethod()213   public void testInterception_ofAllMethodsOnType_interceptsInheritedDefaultMethod() {
214     Injector injector =
215         Guice.createInjector(
216             new AbstractModule() {
217               @Override
218               protected void configure() {
219                 bindInterceptor(Matchers.subclassesOf(BazImpl.class), Matchers.any(), interceptor);
220                 bind(Baz.class).to(BazImpl.class);
221               }
222             });
223 
224     Baz baz = injector.getInstance(Baz.class);
225 
226     assertEquals("Baz", baz.doSomething());
227     assertEquals("BazImpl", baz.doSomethingElse());
228 
229     assertEquals(2, interceptedCallCount.get());
230   }
231 }
232