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();
 }