You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by jc...@apache.org on 2013/08/01 21:57:58 UTC
svn commit: r1509402 - in
/commons/proper/proxy/branches/version-2.0-work/stub/src:
main/java/org/apache/commons/proxy2/stub/
test/java/org/apache/commons/proxy2/stub/
Author: jcarman
Date: Thu Aug 1 19:57:57 2013
New Revision: 1509402
URL: http://svn.apache.org/r1509402
Log:
Refactoring training context to support nested training.
Added:
commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java
commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java
Modified:
commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java
commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/Trainer.java
commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java
commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java
commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java
Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java?rev=1509402&r1=1509401&r2=1509402&view=diff
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java (original)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java Thu Aug 1 19:57:57 2013
@@ -18,13 +18,9 @@
package org.apache.commons.proxy2.stub;
import org.apache.commons.proxy2.Interceptor;
-import org.apache.commons.proxy2.Invoker;
import org.apache.commons.proxy2.ProxyFactory;
-import org.apache.commons.proxy2.ProxyUtils;
import org.apache.commons.proxy2.interceptor.SwitchInterceptor;
-import java.lang.reflect.Method;
-
public class StubInterceptorBuilder
{
//----------------------------------------------------------------------------------------------------------------------
@@ -56,8 +52,8 @@ public class StubInterceptorBuilder
{
try
{
- TrainingContext.set(interceptor);
- T stub = proxyFactory.createInvokerProxy(new TrainingContextInvoker(), type);
+ TrainingContext trainingContext = TrainingContext.set(proxyFactory);
+ T stub = trainingContext.push(type, interceptor);
trainer.train(stub);
}
finally
@@ -66,18 +62,4 @@ public class StubInterceptorBuilder
}
return this;
}
-
-//----------------------------------------------------------------------------------------------------------------------
-// Inner Classes
-//----------------------------------------------------------------------------------------------------------------------
-
- private static final class TrainingContextInvoker implements Invoker
- {
- @Override
- public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable
- {
- TrainingContext.getTrainingContext().stubMethodInvoked(method, arguments);
- return ProxyUtils.nullValue(method.getReturnType());
- }
- }
}
Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/Trainer.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/Trainer.java?rev=1509402&r1=1509401&r2=1509402&view=diff
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/Trainer.java (original)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/Trainer.java Thu Aug 1 19:57:57 2013
@@ -44,7 +44,7 @@ public abstract class Trainer<T>
private void record(ArgumentMatcher matcher)
{
- trainingContext().addArgumentMatcher(matcher);
+ trainingContext().record(matcher);
}
protected <R> R eq(R value)
@@ -61,17 +61,17 @@ public abstract class Trainer<T>
protected void thenThrow(Exception e)
{
- trainingContext().setInterceptor(InterceptorUtils.throwing(e));
+ trainingContext().then(InterceptorUtils.throwing(e));
}
protected void thenThrow(ObjectProvider<? extends Exception> provider)
{
- trainingContext().setInterceptor(InterceptorUtils.throwing(provider));
+ trainingContext().then(InterceptorUtils.throwing(provider));
}
private TrainingContext trainingContext()
{
- return TrainingContext.getTrainingContext();
+ return TrainingContext.getCurrent();
}
protected <R> WhenObject<R> when(R expression)
@@ -130,21 +130,29 @@ public abstract class Trainer<T>
protected abstract class BaseWhen<R>
{
+ protected Trainer<T> thenStub(Class<R> type, Trainer<R> trainer)
+ {
+ R trainee = trainingContext().push(type);
+ trainer.train(trainee);
+ trainingContext().then(InterceptorUtils.constant(trainingContext().popStub(type)));
+ return Trainer.this;
+ }
+
protected Trainer<T> thenThrow(Exception e)
{
- trainingContext().setInterceptor(InterceptorUtils.throwing(e));
+ trainingContext().then(InterceptorUtils.throwing(e));
return Trainer.this;
}
protected Trainer<T> thenThrow(ObjectProvider<? extends Exception> provider)
{
- trainingContext().setInterceptor(InterceptorUtils.throwing(provider));
+ trainingContext().then(InterceptorUtils.throwing(provider));
return Trainer.this;
}
protected <R> Trainer<T> thenAnswer(ObjectProvider<? extends R> provider)
{
- trainingContext().setInterceptor(InterceptorUtils.provider(provider));
+ trainingContext().then(InterceptorUtils.provider(provider));
return Trainer.this;
}
}
@@ -153,7 +161,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(boolean... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -162,7 +170,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(byte... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -171,7 +179,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(char... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -180,7 +188,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(double... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -189,7 +197,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(float... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -198,7 +206,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(int... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -207,7 +215,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(long... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -216,7 +224,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(R value)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(value));
+ trainingContext().then(InterceptorUtils.constant(value));
return Trainer.this;
}
}
@@ -225,7 +233,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(R... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
@@ -234,7 +242,7 @@ public abstract class Trainer<T>
{
protected Trainer<T> thenReturn(short... values)
{
- trainingContext().setInterceptor(InterceptorUtils.constant(ArrayUtils.clone(values)));
+ trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
return Trainer.this;
}
}
Added: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java?rev=1509402&view=auto
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java (added)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java Thu Aug 1 19:57:57 2013
@@ -0,0 +1,223 @@
+package org.apache.commons.proxy2.stub;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.proxy2.*;
+import org.apache.commons.proxy2.interceptor.SwitchInterceptor;
+import org.apache.commons.proxy2.interceptor.matcher.ArgumentMatcher;
+import org.apache.commons.proxy2.interceptor.matcher.InvocationMatcher;
+import org.apache.commons.proxy2.invoker.NullInvoker;
+import org.apache.commons.proxy2.invoker.RecordedInvocation;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+public class TrainingContext
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+ private static final ThreadLocal<TrainingContext> TRAINING_CONTEXT = new ThreadLocal<TrainingContext>();
+
+ private final ProxyFactory proxyFactory;
+
+ private Deque<TrainingContextFrame<?>> frameDeque = new LinkedList<TrainingContextFrame<?>>();
+
+//----------------------------------------------------------------------------------------------------------------------
+// Static Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+ public static void clear()
+ {
+ TRAINING_CONTEXT.remove();
+ }
+
+ public static TrainingContext getCurrent()
+ {
+ return TRAINING_CONTEXT.get();
+ }
+
+ public static TrainingContext set(ProxyFactory proxyFactory)
+ {
+ TrainingContext context = new TrainingContext(proxyFactory);
+ TRAINING_CONTEXT.set(context);
+ return context;
+ }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+ public TrainingContext(ProxyFactory proxyFactory)
+ {
+ this.proxyFactory = proxyFactory;
+ }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+ private TrainingContextFrame<?> peek()
+ {
+ return frameDeque.peek();
+ }
+
+ <T> T popStub(Class<T> type)
+ {
+ return proxyFactory.createInterceptorProxy(
+ proxyFactory.createInvokerProxy(NullInvoker.INSTANCE, type),
+ frameDeque.pop().stubInterceptor,
+ type);
+ }
+
+ <T> T push(Class<T> type)
+ {
+ return push(type, new SwitchInterceptor());
+ }
+
+ <T> T push(Class<T> type, SwitchInterceptor switchInterceptor)
+ {
+ TrainingContextFrame<T> frame = new TrainingContextFrame<T>(switchInterceptor);
+ Invoker invoker = new TrainingInvoker(frame);
+ frameDeque.push(frame);
+ return proxyFactory.createInvokerProxy(invoker, type);
+ }
+
+ public void record(ArgumentMatcher argumentMatcher)
+ {
+ peek().argumentMatchers.add(argumentMatcher);
+ }
+
+ public void then(Interceptor interceptor)
+ {
+ peek().then(interceptor);
+ }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Inner Classes
+//----------------------------------------------------------------------------------------------------------------------
+
+ private static final class ExactArgumentsMatcher implements InvocationMatcher
+ {
+ private final RecordedInvocation recordedInvocation;
+
+ private ExactArgumentsMatcher(RecordedInvocation recordedInvocation)
+ {
+ this.recordedInvocation = recordedInvocation;
+ }
+
+ @Override
+ public boolean matches(Invocation invocation)
+ {
+ return invocation.getMethod().equals(recordedInvocation.getInvokedMethod()) &&
+ Arrays.deepEquals(invocation.getArguments(), recordedInvocation.getArguments());
+ }
+ }
+
+ private static final class MatchingArgumentsMatcher implements InvocationMatcher
+ {
+ private final RecordedInvocation recordedInvocation;
+ private final ArgumentMatcher[] matchers;
+
+ private MatchingArgumentsMatcher(RecordedInvocation recordedInvocation, ArgumentMatcher[] matchers)
+ {
+ this.recordedInvocation = recordedInvocation;
+ this.matchers = ArrayUtils.clone(matchers);
+ }
+
+ @Override
+ public boolean matches(Invocation invocation)
+ {
+ return invocation.getMethod().equals(recordedInvocation.getInvokedMethod()) &&
+ allArgumentsMatch(invocation.getArguments());
+ }
+
+ private boolean allArgumentsMatch(Object[] arguments)
+ {
+ for (int i = 0; i < arguments.length; i++)
+ {
+ Object argument = arguments[i];
+ if (!matchers[i].matches(argument))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static class TrainingContextFrame<T>
+ {
+ private final String id = UUID.randomUUID().toString();
+
+ private final SwitchInterceptor stubInterceptor;
+
+ private final List<ArgumentMatcher> argumentMatchers = new LinkedList<ArgumentMatcher>();
+
+ private InvocationMatcher matcher = null;
+
+ private TrainingContextFrame(SwitchInterceptor stubInterceptor)
+ {
+ this.stubInterceptor = stubInterceptor;
+ }
+
+ private String getId()
+ {
+ return id;
+ }
+
+ void then(Interceptor thenInterceptor)
+ {
+ if (matcher == null)
+ {
+ throw new IllegalStateException("No when!");
+ }
+ stubInterceptor.when(matcher).then(thenInterceptor);
+ matcher = null;
+ }
+
+ void methodInvoked(Method method, Object[] arguments)
+ {
+ final ArgumentMatcher[] matchersArray = argumentMatchers.toArray(new ArgumentMatcher[argumentMatchers.size()]);
+ argumentMatchers.clear();
+ final RecordedInvocation invocation = new RecordedInvocation(method, arguments);
+ if (ArrayUtils.isEmpty(matchersArray))
+ {
+ this.matcher = new ExactArgumentsMatcher(invocation);
+ }
+ else if (matchersArray.length == arguments.length)
+ {
+ this.matcher = new MatchingArgumentsMatcher(invocation, matchersArray);
+ }
+ else
+ {
+ throw new IllegalStateException("Either use exact arguments or argument matchers, but not both.");
+ }
+ }
+ }
+
+ private static class TrainingInvoker implements Invoker
+ {
+ private final String id;
+
+ private TrainingInvoker(TrainingContextFrame<?> frame)
+ {
+ this.id = frame.getId();
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable
+ {
+ final TrainingContextFrame<?> frame = getCurrent().peek();
+ if (!frame.getId().equals(id))
+ {
+ throw new IllegalStateException("Wrong stub!");
+ }
+ else
+ {
+ frame.methodInvoked(method, arguments);
+ }
+ return ProxyUtils.nullValue(method.getReturnType());
+ }
+ }
+}
Added: commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java?rev=1509402&view=auto
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java (added)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java Thu Aug 1 19:57:57 2013
@@ -0,0 +1,17 @@
+package org.apache.commons.proxy2.stub;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RetentionWrapper
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+ Retention value();
+}
Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java?rev=1509402&r1=1509401&r2=1509402&view=diff
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java (original)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java Thu Aug 1 19:57:57 2013
@@ -190,5 +190,11 @@ public class StubBuilderTest
{
}
+
+ @Override
+ public StubInterface stub()
+ {
+ return null;
+ }
}
}
Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java?rev=1509402&r1=1509401&r2=1509402&view=diff
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java (original)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java Thu Aug 1 19:57:57 2013
@@ -25,6 +25,8 @@ import org.apache.commons.proxy2.provide
import org.junit.Before;
import org.junit.Test;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import static org.junit.Assert.*;
@@ -346,4 +348,99 @@ public class StubInterceptorBuilderTest
assertEquals(null, proxy.two("Whatever"));
assertEquals(null, proxy.one("Mismatch!"));
}
+
+ @Test
+ public void testStubReturn()
+ {
+ final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+ {
+ @Override
+ protected void train(StubInterface stub)
+ {
+ when(stub.stub()).thenStub(StubInterface.class, new Trainer<StubInterface>()
+ {
+ @Override
+ protected void train(StubInterface stub)
+ {
+ when(stub.one("Hello")).thenReturn("World");
+ }
+ });
+ }
+ });
+ assertNotNull(proxy.stub());
+ assertEquals("World", proxy.stub().one("Hello"));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testUsingWrongStub()
+ {
+ createProxy(new Trainer<StubInterface>()
+ {
+ @Override
+ protected void train(final StubInterface parent)
+ {
+ when(parent.stub()).thenStub(StubInterface.class, new Trainer<StubInterface>()
+ {
+ @Override
+ protected void train(final StubInterface child)
+ {
+ when(parent.one("Hello")).thenReturn("World");
+ }
+ });
+ }
+ });
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testThenBeforeWhen()
+ {
+ createProxy(new Trainer<StubInterface>()
+ {
+ @Override
+ protected void train(StubInterface stub)
+ {
+ thenThrow(new RuntimeException("Oops!"));
+ }
+ });
+ }
+
+ @Test
+ public void testWithNestedAnnotations()
+ {
+ Interceptor interceptor = builder.trainFor(RetentionWrapper.class, new Trainer<RetentionWrapper>()
+ {
+ @Override
+ protected void train(RetentionWrapper stub)
+ {
+
+ when(stub.value()).thenStub(Retention.class, new Trainer<Retention>()
+ {
+ @Override
+ protected void train(Retention stub)
+ {
+ when(stub.value()).thenReturn(RetentionPolicy.RUNTIME);
+ }
+ });
+ }
+ }).build();
+ RetentionWrapper wrapper = proxyFactory.createInterceptorProxy(proxyFactory.createInvokerProxy(NullInvoker.INSTANCE), interceptor, RetentionWrapper.class);
+ assertNotNull(wrapper.value());
+ assertEquals(RetentionPolicy.RUNTIME, wrapper.value().value());
+ }
+
+ @Test
+ public void testWithSimpleAnnotations()
+ {
+ Interceptor interceptor = builder.trainFor(Retention.class, new Trainer<Retention>()
+ {
+ @Override
+ protected void train(Retention stub)
+ {
+ when(stub.value()).thenReturn(RetentionPolicy.RUNTIME);
+ }
+ }).build();
+ Retention wrapper = proxyFactory.createInterceptorProxy(proxyFactory.createInvokerProxy(NullInvoker.INSTANCE), interceptor, Retention.class);
+ assertEquals(RetentionPolicy.RUNTIME, wrapper.value());
+ }
+
}
Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java?rev=1509402&r1=1509401&r2=1509402&view=diff
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java (original)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java Thu Aug 1 19:57:57 2013
@@ -40,4 +40,6 @@ public interface StubInterface
public String arrayParameter(String... strings);
public void voidMethod(String arg);
+
+ public StubInterface stub();
}