Files
guice/core/test/com/google/inject/MethodInterceptionTest.java
T
limpbizkit@gmail.com e0f541287d Remove AOP boilerplate from user visible stack traces.
We received complaints because AOP stack traces are difficult to follow. This removes almost everything. The weird thing about this change is that the stack traces contain gaps. Anyone tracing through a stack trace should recognize that proceed() or invoke() means that AOP is in play.

The stack trace of a method that was subject to two interceptors used to look like this:

java.lang.Exception: kaboom!
	at com.publicobject.Interceptable.explode(Interceptable.java:203)
	at com.publicobject.Interceptable$$EnhancerByGuice$$5ffccef9.CGLIB$explode$2(<generated>)
	at com.publicobject.Interceptable$$EnhancerByGuice$$5ffccef9$$FastClassByGuice$$20c8faff.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
	at com.publicobject.InterceptorB.invoke(InterceptorB.java:57)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
	at com.publicobject.InterceptorA.invoke(InterceptorA.java:50)
	at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
	at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:52)
	at com.publicobject.MyTest$Interceptable$$EnhancerByGuice$$5ffccef9.explode(<generated>)
	at com.publicobject.MyTest.testInterceptedMethodThrows(MyTest.java:181)

Now it looks like this:

java.lang.Exception: kaboom!
	at com.publicobject.Interceptable.explode(Interceptable.java:203)
	at com.publicobject.InterceptorB.invoke(InterceptorB.java:57)
	at com.publicobject.InterceptorA.invoke(InterceptorA.java:50)
	at com.publicobject.MyTest.testInterceptedMethodThrows(MyTest.java:181)


git-svn-id: https://google-guice.googlecode.com/svn/trunk@1481 d779f126-a31b-0410-b53b-1d3aecad763e
2011-01-15 07:06:25 +00:00

195 lines
7.0 KiB
Java

/**
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.inject;
import com.google.inject.internal.util.ImmutableList;
import com.google.inject.internal.util.ImmutableMap;
import com.google.inject.internal.util.Iterables;
import com.google.inject.matcher.Matchers;
import static com.google.inject.matcher.Matchers.only;
import com.google.inject.spi.ConstructorBinding;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import junit.framework.TestCase;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* @author jessewilson@google.com (Jesse Wilson)
*/
public class MethodInterceptionTest extends TestCase {
private AtomicInteger count = new AtomicInteger();
private final class CountingInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
count.incrementAndGet();
return methodInvocation.proceed();
}
}
private final class ReturnNullInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return null;
}
}
public void testSharedProxyClasses() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.returns(only(Foo.class)),
new ReturnNullInterceptor());
}
});
Injector nullFoosInjector = injector.createChildInjector(new AbstractModule() {
protected void configure() {
bind(Interceptable.class);
}
});
Interceptable nullFoos = nullFoosInjector.getInstance(Interceptable.class);
assertNotNull(nullFoos.bar());
assertNull(nullFoos.foo());
Injector nullFoosAndBarsInjector = injector.createChildInjector(new AbstractModule() {
protected void configure() {
bind(Interceptable.class);
bindInterceptor(Matchers.any(), Matchers.returns(only(Bar.class)),
new ReturnNullInterceptor());
}
});
Interceptable bothNull = nullFoosAndBarsInjector.getInstance(Interceptable.class);
assertNull(bothNull.bar());
assertNull(bothNull.foo());
assertSame("Child injectors should share proxy classes, otherwise memory leaks!",
nullFoos.getClass(), bothNull.getClass());
}
public void testGetThis() {
final AtomicReference<Object> lastTarget = new AtomicReference<Object>();
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.any(), new MethodInterceptor() {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
lastTarget.set(methodInvocation.getThis());
return methodInvocation.proceed();
}
});
}
});
Interceptable interceptable = injector.getInstance(Interceptable.class);
interceptable.foo();
assertSame(interceptable, lastTarget.get());
}
public void testInterceptingFinalClass() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.any(), new MethodInterceptor() {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return methodInvocation.proceed();
}
});
}
});
try {
injector.getInstance(NotInterceptable.class);
fail();
} catch(ConfigurationException ce) {
assertEquals("Unable to method intercept: " + NotInterceptable.class.getName(),
Iterables.getOnlyElement(ce.getErrorMessages()).getMessage().toString());
assertEquals("Cannot subclass final class class " + NotInterceptable.class.getName(),
ce.getCause().getMessage());
}
}
public void testSpiAccessToInterceptors() throws NoSuchMethodException {
final MethodInterceptor countingInterceptor = new CountingInterceptor();
final MethodInterceptor returnNullInterceptor = new ReturnNullInterceptor();
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindInterceptor(Matchers.any(),Matchers.returns(only(Foo.class)),
countingInterceptor);
bindInterceptor(Matchers.any(), Matchers.returns(only(Foo.class).or(only(Bar.class))),
returnNullInterceptor);
}
});
ConstructorBinding<?> interceptedBinding
= (ConstructorBinding<?>) injector.getBinding(Interceptable.class);
Method barMethod = Interceptable.class.getMethod("bar");
Method fooMethod = Interceptable.class.getMethod("foo");
assertEquals(ImmutableMap.<Method, List<MethodInterceptor>>of(
fooMethod, ImmutableList.of(countingInterceptor, returnNullInterceptor),
barMethod, ImmutableList.of(returnNullInterceptor)),
interceptedBinding.getMethodInterceptors());
ConstructorBinding<?> nonInterceptedBinding
= (ConstructorBinding<?>) injector.getBinding(Foo.class);
assertEquals(ImmutableMap.<Method, List<MethodInterceptor>>of(),
nonInterceptedBinding.getMethodInterceptors());
injector.getInstance(Interceptable.class).foo();
assertEquals("expected counting interceptor to be invoked first", 1, count.get());
}
public void testInterceptedMethodThrows() throws Exception {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.any(), new CountingInterceptor());
bindInterceptor(Matchers.any(), Matchers.any(), new CountingInterceptor());
}
});
Interceptable interceptable = injector.getInstance(Interceptable.class);
try {
interceptable.explode();
fail();
} catch (Exception e) {
StackTraceElement[] stackTraceElement = e.getStackTrace();
assertEquals("explode", stackTraceElement[0].getMethodName());
assertEquals("invoke", stackTraceElement[1].getMethodName());
assertEquals("invoke", stackTraceElement[2].getMethodName());
assertEquals("testInterceptedMethodThrows", stackTraceElement[3].getMethodName());
}
}
static class Foo {}
static class Bar {}
public static class Interceptable {
public Foo foo() {
return new Foo() {};
}
public Bar bar() {
return new Bar() {};
}
public String explode() throws Exception {
throw new Exception("kaboom!");
}
}
public static final class NotInterceptable {}
}