make 2.0 branch into trunk

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/proxy/trunk@1579340 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/NOTICE.txt b/NOTICE.txt
index b8e091f..0075441 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1,5 +1,5 @@
 Apache Commons Proxy
 Copyright 2005-2008 The Apache Software Foundation
 
-This product includes software developed at
-The Apache Software Foundation (http://www.apache.org/).
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
\ No newline at end of file
diff --git a/PROPOSAL.html b/PROPOSAL.html
index 82499cd..ddaa1fd 100644
--- a/PROPOSAL.html
+++ b/PROPOSAL.html
@@ -6,7 +6,7 @@
   ~ (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
+  ~      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,
@@ -14,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
+
 <html>
 <head>
     <title>Proposal for Proxy Package</title>
diff --git a/asm4/pom.xml b/asm4/pom.xml
new file mode 100644
index 0000000..90318f5
--- /dev/null
+++ b/asm4/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>commons-proxy2-parent</artifactId>
+        <groupId>org.apache.commons</groupId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>commons-proxy2-asm4</artifactId>
+    <name>Commons Proxy ASM4 Proxies Module</name>
+    <description>Proxies based on classes dynamically generated using ASM v4
+    </description>
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm-commons</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java b/asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java
new file mode 100644
index 0000000..57ed58f
--- /dev/null
+++ b/asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java
@@ -0,0 +1,470 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.asm4;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ObjectProvider;
+import org.apache.commons.proxy2.ProxyUtils;
+import org.apache.commons.proxy2.exception.ProxyFactoryException;
+import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator;
+import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory;
+import org.apache.commons.proxy2.impl.ProxyClassCache;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+public class ASM4ProxyFactory extends AbstractSubclassingProxyFactory
+{
+    private static final ProxyClassCache PROXY_CLASS_CACHE = new ProxyClassCache(new ProxyGenerator());
+
+    @Override
+    public <T> T createDelegatorProxy(final ClassLoader classLoader, final ObjectProvider<?> delegateProvider,
+            final Class<?>... proxyClasses)
+    {
+        return createProxy(classLoader, new DelegatorInvoker(delegateProvider), proxyClasses);
+    }
+
+    @Override
+    public <T> T createInterceptorProxy(final ClassLoader classLoader, final Object target,
+            final Interceptor interceptor, final Class<?>... proxyClasses)
+    {
+        return createProxy(classLoader, new InterceptorInvoker(target, interceptor), proxyClasses);
+    }
+
+    @Override
+    public <T> T createInvokerProxy(final ClassLoader classLoader, final Invoker invoker,
+            final Class<?>... proxyClasses)
+    {
+        return createProxy(classLoader, new InvokerInvoker(invoker), proxyClasses);
+    }
+
+    private <T> T createProxy(final ClassLoader classLoader, final AbstractInvoker invoker,
+            final Class<?>... proxyClasses)
+    {
+        final Class<?> proxyClass = PROXY_CLASS_CACHE.getProxyClass(classLoader, proxyClasses);
+        try
+        {
+            @SuppressWarnings("unchecked")
+            final T result = (T) proxyClass.getConstructor(Invoker.class).newInstance(invoker);
+            return result;
+        }
+        catch (Exception e)
+        {
+            throw e instanceof RuntimeException ? ((RuntimeException) e) : new RuntimeException(e);
+        }
+    }
+
+    private static class ProxyGenerator extends AbstractProxyClassGenerator implements Opcodes
+    {
+        private static final AtomicInteger CLASS_NUMBER = new AtomicInteger(0);
+        private static final String CLASSNAME_PREFIX = "CommonsProxyASM4_";
+        private static final String HANDLER_NAME = "__handler";
+        private static final Type INVOKER_TYPE = Type.getType(Invoker.class);
+
+        @Override
+        public Class<?> generateProxyClass(final ClassLoader classLoader, final Class<?>... proxyClasses)
+        {
+            final Class<?> superclass = getSuperclass(proxyClasses);
+            final String proxyName = CLASSNAME_PREFIX + CLASS_NUMBER.incrementAndGet();
+            final Method[] implementationMethods = getImplementationMethods(proxyClasses);
+            final Class<?>[] interfaces = toInterfaces(proxyClasses);
+            final String classFileName = proxyName.replace('.', '/');
+
+            try
+            {
+                final byte[] proxyBytes = generateProxy(superclass, classFileName, implementationMethods, interfaces);
+                return loadClass(classLoader, proxyName, proxyBytes);
+            }
+            catch (final Exception e)
+            {
+                throw new ProxyFactoryException(e);
+            }
+        }
+
+        private static byte[] generateProxy(final Class<?> classToProxy, final String proxyName,
+                final Method[] methods, final Class<?>... interfaces) throws ProxyFactoryException
+        {
+            final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+
+            final Type proxyType = Type.getObjectType(proxyName);
+
+            // push class signature
+            final String[] interfaceNames = new String[interfaces.length];
+            for (int i = 0; i < interfaces.length; i++)
+            {
+                interfaceNames[i] = Type.getType(interfaces[i]).getInternalName();
+            }
+
+            final Type superType = Type.getType(classToProxy);
+            cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, proxyType.getInternalName(), null, superType.getInternalName(),
+                    interfaceNames);
+
+            // create Invoker field
+            cw.visitField(ACC_FINAL + ACC_PRIVATE, HANDLER_NAME, INVOKER_TYPE.getDescriptor(), null, null).visitEnd();
+
+            init(cw, proxyType, superType);
+
+            for (final Method method : methods)
+            {
+                processMethod(cw, method, proxyType, HANDLER_NAME);
+            }
+
+            return cw.toByteArray();
+        }
+
+        private static void init(final ClassWriter cw, final Type proxyType, Type superType)
+        {
+            final GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, new org.objectweb.asm.commons.Method("<init>",
+                    Type.VOID_TYPE, new Type[] { INVOKER_TYPE }), null, null, cw);
+            // invoke super constructor:
+            mg.loadThis();
+            mg.invokeConstructor(superType, org.objectweb.asm.commons.Method.getMethod("void <init> ()"));
+
+            // assign handler:
+            mg.loadThis();
+            mg.loadArg(0);
+            mg.putField(proxyType, HANDLER_NAME, INVOKER_TYPE);
+            mg.returnValue();
+            mg.endMethod();
+        }
+
+        private static void processMethod(final ClassWriter cw, final Method method, final Type proxyType,
+                final String handlerName) throws ProxyFactoryException
+        {
+            final Type sig = Type.getType(method);
+            final Type[] exceptionTypes = getTypes(method.getExceptionTypes());
+
+            // push the method definition
+            final int access = (ACC_PUBLIC | ACC_PROTECTED) & method.getModifiers();
+            final org.objectweb.asm.commons.Method m = org.objectweb.asm.commons.Method.getMethod(method);
+            final GeneratorAdapter mg = new GeneratorAdapter(access, m, null, getTypes(method.getExceptionTypes()), cw);
+
+            final Label tryBlock = exceptionTypes.length > 0 ? mg.mark() : null;
+
+            mg.push(Type.getType(method.getDeclaringClass()));
+
+            // the following code generates the bytecode for this line of Java:
+            // Method method = <proxy>.class.getMethod("add", new Class[] {
+            // <array of function argument classes> });
+
+            // get the method name to invoke, and push to stack
+
+            mg.push(method.getName());
+
+            // create the Class[]
+            mg.push(sig.getArgumentTypes().length);
+            final Type classType = Type.getType(Class.class);
+            mg.newArray(classType);
+
+            // push parameters into array
+            for (int i = 0; i < sig.getArgumentTypes().length; i++)
+            {
+                // keep copy of array on stack
+                mg.dup();
+
+                // push index onto stack
+                mg.push(i);
+                mg.push(sig.getArgumentTypes()[i]);
+                mg.arrayStore(classType);
+            }
+
+            // invoke getMethod() with the method name and the array of types
+            mg.invokeVirtual(classType, org.objectweb.asm.commons.Method
+                    .getMethod("java.lang.reflect.Method getDeclaredMethod(String, Class[])"));
+            // store the returned method for later
+
+            // the following code generates bytecode equivalent to:
+            // return ((<returntype>) invoker.invoke(this, method, new Object[]
+            // { <function arguments }))[.<primitive>Value()];
+
+            mg.loadThis();
+
+            mg.getField(proxyType, handlerName, INVOKER_TYPE);
+            // put below method:
+            mg.swap();
+
+            // we want to pass "this" in as the first parameter
+            mg.loadThis();
+            // put below method:
+            mg.swap();
+
+            // need to construct the array of objects passed in
+
+            // create the Object[]
+            mg.push(sig.getArgumentTypes().length);
+            final Type objectType = Type.getType(Object.class);
+            mg.newArray(objectType);
+
+            // push parameters into array
+            for (int i = 0; i < sig.getArgumentTypes().length; i++)
+            {
+                // keep copy of array on stack
+                mg.dup();
+
+                // push index onto stack
+                mg.push(i);
+
+                mg.loadArg(i);
+                mg.valueOf(sig.getArgumentTypes()[i]);
+                mg.arrayStore(objectType);
+            }
+
+            // invoke the invoker
+            mg.invokeInterface(INVOKER_TYPE, org.objectweb.asm.commons.Method
+                    .getMethod("Object invoke(Object, java.lang.reflect.Method, Object[])"));
+
+            // cast the result
+            mg.unbox(sig.getReturnType());
+
+            // push return
+            mg.returnValue();
+
+            // catch InvocationTargetException
+            if (exceptionTypes.length > 0)
+            {
+                final Type caughtExceptionType = Type.getType(InvocationTargetException.class);
+                mg.catchException(tryBlock, mg.mark(), caughtExceptionType);
+
+                final Label throwCause = new Label();
+
+                mg.invokeVirtual(caughtExceptionType,
+                        org.objectweb.asm.commons.Method.getMethod("Throwable getCause()"));
+
+                for (int i = 0; i < exceptionTypes.length; i++)
+                {
+                    mg.dup();
+                    mg.push(exceptionTypes[i]);
+                    mg.swap();
+                    mg.invokeVirtual(classType,
+                            org.objectweb.asm.commons.Method.getMethod("boolean isInstance(Object)"));
+                    // if true, throw cause:
+                    mg.ifZCmp(GeneratorAdapter.NE, throwCause);
+                }
+                // no exception types matched; throw
+                // UndeclaredThrowableException:
+                final int cause = mg.newLocal(Type.getType(Exception.class));
+                mg.storeLocal(cause);
+                final Type undeclaredType = Type.getType(UndeclaredThrowableException.class);
+                mg.newInstance(undeclaredType);
+                mg.dup();
+                mg.loadLocal(cause);
+                mg.invokeConstructor(undeclaredType, new org.objectweb.asm.commons.Method("<init>", Type.VOID_TYPE,
+                        new Type[] { Type.getType(Throwable.class) }));
+                mg.throwException();
+
+                mg.mark(throwCause);
+                mg.throwException();
+            }
+
+            // finish this method
+            mg.endMethod();
+        }
+
+        private static Type[] getTypes(Class<?>... src)
+        {
+            final Type[] result = new Type[src.length];
+            for (int i = 0; i < result.length; i++)
+            {
+                result[i] = Type.getType(src[i]);
+            }
+            return result;
+        }
+
+        /**
+         * Adapted from http://asm.ow2.org/doc/faq.html#Q5
+         * 
+         * @param b
+         * @return Class<?>
+         */
+        private static Class<?> loadClass(final ClassLoader loader, String className, byte[] b)
+        {
+            // override classDefine (as it is protected) and define the class.
+            try
+            {
+                final Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class,
+                        int.class, int.class);
+
+                // protected method invocation
+                final boolean accessible = method.isAccessible();
+                if (!accessible)
+                {
+                    method.setAccessible(true);
+                }
+                try
+                {
+                    return (Class<?>) method
+                            .invoke(loader, className, b, Integer.valueOf(0), Integer.valueOf(b.length));
+                }
+                finally
+                {
+                    if (!accessible)
+                    {
+                        method.setAccessible(false);
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                throw e instanceof RuntimeException ? ((RuntimeException) e) : new RuntimeException(e);
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    private static class DelegatorInvoker extends AbstractInvoker
+    {
+        private final ObjectProvider<?> delegateProvider;
+
+        protected DelegatorInvoker(ObjectProvider<?> delegateProvider)
+        {
+            this.delegateProvider = delegateProvider;
+        }
+
+        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
+        {
+            try
+            {
+                return method.invoke(delegateProvider.getObject(), args);
+            }
+            catch (InvocationTargetException e)
+            {
+                throw e.getTargetException();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    private static class InterceptorInvoker extends AbstractInvoker
+    {
+        private final Object target;
+        private final Interceptor methodInterceptor;
+
+        public InterceptorInvoker(Object target, Interceptor methodInterceptor)
+        {
+            this.target = target;
+            this.methodInterceptor = methodInterceptor;
+        }
+
+        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
+        {
+            final ReflectionInvocation invocation = new ReflectionInvocation(target, proxy, method, args);
+            return methodInterceptor.intercept(invocation);
+        }
+    }
+
+    @SuppressWarnings("serial")
+    private abstract static class AbstractInvoker implements Invoker, Serializable
+    {
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+        {
+            if (isHashCode(method))
+            {
+                return System.identityHashCode(proxy);
+            }
+            if (isEqualsMethod(method))
+            {
+                return proxy == args[0];
+            }
+            return invokeImpl(proxy, method, args);
+        }
+
+        protected abstract Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable;
+    }
+
+    @SuppressWarnings("serial")
+    private static class InvokerInvoker extends AbstractInvoker
+    {
+        private final Invoker invoker;
+
+        public InvokerInvoker(Invoker invoker)
+        {
+            this.invoker = invoker;
+        }
+
+        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
+        {
+            return invoker.invoke(proxy, method, args);
+        }
+    }
+
+    protected static boolean isHashCode(Method method)
+    {
+        return "hashCode".equals(method.getName()) && Integer.TYPE.equals(method.getReturnType())
+                && method.getParameterTypes().length == 0;
+    }
+
+    protected static boolean isEqualsMethod(Method method)
+    {
+        return "equals".equals(method.getName()) && Boolean.TYPE.equals(method.getReturnType())
+                && method.getParameterTypes().length == 1 && Object.class.equals(method.getParameterTypes()[0]);
+    }
+
+    @SuppressWarnings("serial")
+    private static class ReflectionInvocation implements Invocation, Serializable
+    {
+        private final Method method;
+        private final Object[] arguments;
+        private final Object proxy;
+        private final Object target;
+
+        public ReflectionInvocation(final Object target, final Object proxy, final Method method,
+                final Object[] arguments)
+        {
+            this.method = method;
+            this.arguments = (arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments);
+            this.proxy = proxy;
+            this.target = target;
+        }
+
+        public Object[] getArguments()
+        {
+            return arguments;
+        }
+
+        public Method getMethod()
+        {
+            return method;
+        }
+
+        public Object getProxy()
+        {
+            return proxy;
+        }
+
+        public Object proceed() throws Throwable
+        {
+            try
+            {
+                return method.invoke(target, arguments);
+            }
+            catch (InvocationTargetException e)
+            {
+                throw e.getTargetException();
+            }
+        }
+    }
+}
diff --git a/src/test/resources/log4j.properties b/asm4/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
similarity index 75%
copy from src/test/resources/log4j.properties
copy to asm4/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
index b981287..529137c 100644
--- a/src/test/resources/log4j.properties
+++ b/asm4/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
@@ -14,7 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-log4j.rootLogger=DEBUG, console
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern=%d{MM-dd@HH:mm:ss} %-5p (%c{1}) %3x - %m%n
\ No newline at end of file
+org.apache.commons.proxy2.asm4.ASM4ProxyFactory
diff --git a/asm4/src/site/markdown/index.md b/asm4/src/site/markdown/index.md
new file mode 100644
index 0000000..10fc4cd
--- /dev/null
+++ b/asm4/src/site/markdown/index.md
@@ -0,0 +1,28 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+-->
+
+## Commons Proxy ASM4
+
+Provides the [ASM4ProxyFactory][] which uses the [ASM][] library (v4.x)
+to create proxy classes. This proxy factory is capable of proxying concrete
+non-`final` types and can thus be considered a *subclassing* proxy factory.
+
+[ASM4ProxyFactory]: apidocs/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.html
+
+[ASM]: http://asm.ow2.org/
diff --git a/asm4/src/test/java/org/apache/commons/proxy2/asm4/TestAsm4ProxyFactory.java b/asm4/src/test/java/org/apache/commons/proxy2/asm4/TestAsm4ProxyFactory.java
new file mode 100644
index 0000000..7808e84
--- /dev/null
+++ b/asm4/src/test/java/org/apache/commons/proxy2/asm4/TestAsm4ProxyFactory.java
@@ -0,0 +1,10 @@
+package org.apache.commons.proxy2.asm4;
+
+import org.apache.commons.proxy2.AbstractSubclassingProxyFactoryTestCase;
+
+public class TestAsm4ProxyFactory extends AbstractSubclassingProxyFactoryTestCase
+{
+//**********************************************************************************************************************
+// Constructors
+//**********************************************************************************************************************
+}
diff --git a/build-tools/pom.xml b/build-tools/pom.xml
new file mode 100644
index 0000000..e2996ab
--- /dev/null
+++ b/build-tools/pom.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <!--
+        ~ Licensed to the Apache Software Foundation (ASF) under one or more ~
+        contributor license agreements. See the NOTICE file distributed with ~
+        this work for additional information regarding copyright ownership. ~
+        The ASF licenses this file to You 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.
+    -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-proxy2-parent</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>commons-proxy2-build-tools</artifactId>
+    <name>Commons Proxy Build Tools</name>
+    <description>Provide common setup, from http://maven.apache.org/plugins/maven-checkstyle-plugin/examples/multi-module-config.html</description>
+</project>
diff --git a/build-tools/src/main/resources/org/apache/commons/proxy2/checkstyle.xml b/build-tools/src/main/resources/org/apache/commons/proxy2/checkstyle.xml
new file mode 100644
index 0000000..0ed8c5b
--- /dev/null
+++ b/build-tools/src/main/resources/org/apache/commons/proxy2/checkstyle.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->     
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<!-- commons proxy2 customization of default Checkstyle behavior -->
+<module name="Checker">
+  <property name="localeLanguage" value="en"/>
+
+  <module name="Header">
+    <property name="headerFile" value="${checkstyle.header.file}"/>
+  </module>
+
+  <!-- no tabs allowed in files -->
+  <module name="FileTabCharacter"/>
+
+  <module name="TreeWalker">
+    <!-- Verify that EVERY source file has the appropriate license -->
+    <!-- check sane import statements -->
+    <module name="AvoidStarImport"/>
+    <module name="RedundantImport"/>
+    <module name="UnusedImports"/>
+
+    <module name="LineLength">
+      <property name="max" value="120"/>
+    </module>
+  </module>
+</module>
diff --git a/license-header.txt b/build-tools/src/main/resources/org/apache/commons/proxy2/license-header.txt
similarity index 100%
rename from license-header.txt
rename to build-tools/src/main/resources/org/apache/commons/proxy2/license-header.txt
diff --git a/cglib/pom.xml b/cglib/pom.xml
new file mode 100644
index 0000000..ce16287
--- /dev/null
+++ b/cglib/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>commons-proxy2-parent</artifactId>
+        <groupId>org.apache.commons</groupId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>commons-proxy2-cglib</artifactId>
+    <name>Commons Proxy CGLIB Proxies Module</name>
+    <description>Proxies based on classes dynamically generated using cglib
+    </description>
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/src/main/java/org/apache/commons/proxy/factory/cglib/CglibProxyFactory.java b/cglib/src/main/java/org/apache/commons/proxy2/cglib/CglibProxyFactory.java
similarity index 67%
rename from src/main/java/org/apache/commons/proxy/factory/cglib/CglibProxyFactory.java
rename to cglib/src/main/java/org/apache/commons/proxy2/cglib/CglibProxyFactory.java
index 09aa42d..623e64b 100644
--- a/src/main/java/org/apache/commons/proxy/factory/cglib/CglibProxyFactory.java
+++ b/cglib/src/main/java/org/apache/commons/proxy2/cglib/CglibProxyFactory.java
@@ -1,218 +1,232 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.cglib;
-
-import net.sf.cglib.proxy.Callback;
-import net.sf.cglib.proxy.CallbackFilter;
-import net.sf.cglib.proxy.Dispatcher;
-import net.sf.cglib.proxy.Enhancer;
-import net.sf.cglib.proxy.MethodProxy;
-import net.sf.cglib.proxy.MethodInterceptor;
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-import org.apache.commons.proxy.Invoker;
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.factory.util.AbstractSubclassingProxyFactory;
-
-import java.io.Serializable;
-import java.lang.reflect.Method;
-
-/**
- * A <a href="http://cglib.sourceforge.net/">CGLIB</a>-based {@link org.apache.commons.proxy.ProxyFactory}
- * implementation.
- * <p/>
- * <p/>
- * <b>Dependencies</b>: <ul> <li>CGLIB version 2.0.2 or greater</li> </ul> </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class CglibProxyFactory extends AbstractSubclassingProxyFactory
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static CallbackFilter callbackFilter = new CglibProxyFactoryCallbackFilter();
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public Object createDelegatorProxy(ClassLoader classLoader, ObjectProvider targetProvider,
-                                       Class[] proxyClasses)
-    {
-        final Enhancer enhancer = new Enhancer();
-        enhancer.setClassLoader(classLoader);
-        enhancer.setInterfaces(toInterfaces(proxyClasses));
-        enhancer.setSuperclass(getSuperclass(proxyClasses));
-        enhancer.setCallbackFilter(callbackFilter);
-        enhancer.setCallbacks(new Callback[]{new ObjectProviderDispatcher(targetProvider), new EqualsHandler(), new HashCodeHandler()});
-        return enhancer.create();
-    }
-
-    public Object createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
-                                         Class[] proxyClasses)
-    {
-        final Enhancer enhancer = new Enhancer();
-        enhancer.setClassLoader(classLoader);
-        enhancer.setInterfaces(toInterfaces(proxyClasses));
-        enhancer.setSuperclass(getSuperclass(proxyClasses));
-        enhancer.setCallbackFilter(callbackFilter);
-        enhancer.setCallbacks(new Callback[]{new InterceptorBridge(target, interceptor), new EqualsHandler(), new HashCodeHandler()});
-        return enhancer.create();
-    }
-
-    public Object createInvokerProxy(ClassLoader classLoader, Invoker invoker,
-                                     Class[] proxyClasses)
-    {
-        final Enhancer enhancer = new Enhancer();
-        enhancer.setClassLoader(classLoader);
-        enhancer.setInterfaces(toInterfaces(proxyClasses));
-        enhancer.setSuperclass(getSuperclass(proxyClasses));
-        enhancer.setCallbackFilter(callbackFilter);
-        enhancer.setCallbacks(new Callback[]{new InvokerBridge(invoker), new EqualsHandler(), new HashCodeHandler()});
-        return enhancer.create();
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private static class InterceptorBridge implements net.sf.cglib.proxy.MethodInterceptor, Serializable
-    {
-        private final Interceptor inner;
-        private final Object target;
-
-        public InterceptorBridge(Object target, Interceptor inner)
-        {
-            this.inner = inner;
-            this.target = target;
-        }
-
-        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
-        {
-            return inner.intercept(new MethodProxyInvocation(target, method, args, methodProxy));
-        }
-    }
-
-    private static class HashCodeHandler implements MethodInterceptor, Serializable
-    {
-        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
-        {
-            return new Integer(System.identityHashCode(o));
-        }
-    }
-
-    private static class EqualsHandler implements MethodInterceptor, Serializable
-    {
-        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
-        {
-            return Boolean.valueOf(o == objects[0]);
-        }
-    }
-
-
-    private static class InvokerBridge implements net.sf.cglib.proxy.InvocationHandler, Serializable
-    {
-        private final Invoker original;
-
-        public InvokerBridge(Invoker original)
-        {
-            this.original = original;
-        }
-
-        public Object invoke(Object object, Method method, Object[] objects) throws Throwable
-        {
-            return original.invoke(object, method, objects);
-        }
-    }
-
-    private static class MethodProxyInvocation implements Invocation, Serializable
-    {
-        private final MethodProxy methodProxy;
-        private final Method method;
-        private final Object[] args;
-        private final Object target;
-
-        public MethodProxyInvocation(Object target, Method method, Object[] args, MethodProxy methodProxy)
-        {
-            this.target = target;
-            this.method = method;
-            this.methodProxy = methodProxy;
-            this.args = args;
-        }
-
-        public Method getMethod()
-        {
-            return method;
-        }
-
-        public Object[] getArguments()
-        {
-            return args;
-        }
-
-        public Object proceed() throws Throwable
-        {
-            return methodProxy.invoke(target, args);
-        }
-
-        public Object getProxy()
-        {
-            return target;
-        }
-    }
-
-    private static class ObjectProviderDispatcher implements Dispatcher, Serializable
-    {
-        private final ObjectProvider delegateProvider;
-
-        public ObjectProviderDispatcher(ObjectProvider delegateProvider)
-        {
-            this.delegateProvider = delegateProvider;
-        }
-
-        public Object loadObject()
-        {
-            return delegateProvider.getObject();
-        }
-    }
-
-    private static class CglibProxyFactoryCallbackFilter implements CallbackFilter
-    {
-        public int accept(Method method)
-        {
-            if (isEqualsMethod(method))
-            {
-                return 1;
-            }
-            else if (isHashCode(method))
-            {
-                return 2;
-            }
-            else
-            {
-                return 0;
-            }
-        }
-
-
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.cglib;

+

+import net.sf.cglib.proxy.*;

+import org.apache.commons.lang3.ArrayUtils;

+import org.apache.commons.proxy2.*;

+import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory;

+

+import java.io.Serializable;

+import java.lang.reflect.Method;

+

+/**

+ * Cglib-based {@link ProxyFactory} implementation.

+ */

+public class CglibProxyFactory extends AbstractSubclassingProxyFactory

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private static CallbackFilter callbackFilter = new CglibProxyFactoryCallbackFilter();

+

+  //**********************************************************************************************************************

+ // ProxyFactory Implementation

+ //**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> targetProvider,

+                                       Class<?>... proxyClasses)

+    {

+        final Enhancer enhancer = new Enhancer();

+        enhancer.setClassLoader(classLoader);

+        enhancer.setInterfaces(toInterfaces(proxyClasses));

+        enhancer.setSuperclass(getSuperclass(proxyClasses));

+        enhancer.setCallbackFilter(callbackFilter);

+        enhancer.setCallbacks(new Callback[]{new ObjectProviderDispatcher(targetProvider), new EqualsHandler(), new HashCodeHandler()});

+        return (T) enhancer.create();

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,

+                                         Class<?>... proxyClasses)

+    {

+        final Enhancer enhancer = new Enhancer();

+        enhancer.setClassLoader(classLoader);

+        enhancer.setInterfaces(toInterfaces(proxyClasses));

+        enhancer.setSuperclass(getSuperclass(proxyClasses));

+        enhancer.setCallbackFilter(callbackFilter);

+        enhancer.setCallbacks(new Callback[]{new InterceptorBridge(target, interceptor), new EqualsHandler(), new HashCodeHandler()});

+        return (T) enhancer.create();

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker,

+                                     Class<?>... proxyClasses)

+    {

+        final Enhancer enhancer = new Enhancer();

+        enhancer.setClassLoader(classLoader);

+        enhancer.setInterfaces(toInterfaces(proxyClasses));

+        enhancer.setSuperclass(getSuperclass(proxyClasses));

+        enhancer.setCallbackFilter(callbackFilter);

+        enhancer.setCallbacks(new Callback[]{new InvokerBridge(invoker), new EqualsHandler(), new HashCodeHandler()});

+        return (T) enhancer.create();

+    }

+

+//**********************************************************************************************************************

+// Inner Classes

+//**********************************************************************************************************************

+

+    private static class CglibProxyFactoryCallbackFilter implements CallbackFilter

+    {

+        public int accept(Method method)

+        {

+            if (ProxyUtils.isEqualsMethod(method))

+            {

+                return 1;

+            }

+            else if (ProxyUtils.isHashCode(method))

+            {

+                return 2;

+            }

+            else

+            {

+                return 0;

+            }

+        }

+    }

+

+    private static class EqualsHandler implements MethodInterceptor, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable

+        {

+            return o == objects[0];

+        }

+    }

+

+    private static class HashCodeHandler implements MethodInterceptor, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable

+        {

+            return System.identityHashCode(o);

+        }

+    }

+

+    private static class InterceptorBridge implements net.sf.cglib.proxy.MethodInterceptor, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final Object target;

+        private final Interceptor inner;

+

+        public InterceptorBridge(Object target, Interceptor inner)

+        {

+            this.inner = inner;

+            this.target = target;

+        }

+

+        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable

+        {

+            return inner.intercept(new MethodProxyInvocation(object, target, method, args, methodProxy));

+        }

+    }

+

+    private static class InvokerBridge implements net.sf.cglib.proxy.InvocationHandler, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final Invoker original;

+

+        public InvokerBridge(Invoker original)

+        {

+            this.original = original;

+        }

+

+        public Object invoke(Object object, Method method, Object[] objects) throws Throwable

+        {

+            return original.invoke(object, method, objects);

+        }

+    }

+

+    private static class MethodProxyInvocation implements Invocation, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final Object proxy;

+        private final Object target;

+        private final Method method;

+        private final Object[] args;

+        private final MethodProxy methodProxy;

+

+        public MethodProxyInvocation(Object proxy, Object target, Method method, Object[] args, MethodProxy methodProxy)

+        {

+            this.proxy = proxy;

+            this.target = target;

+            this.method = method;

+            this.methodProxy = methodProxy;

+            this.args = ArrayUtils.clone(args);

+        }

+

+        public Method getMethod()

+        {

+            return method;

+        }

+

+        public Object[] getArguments()

+        {

+            return args;

+        }

+

+        public Object proceed() throws Throwable

+        {

+            return methodProxy.invoke(target, args);

+        }

+

+        public Object getProxy()

+        {

+            return proxy;

+        }

+    }

+

+    private static class ObjectProviderDispatcher implements Dispatcher, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final ObjectProvider<?> delegateProvider;

+

+        public ObjectProviderDispatcher(ObjectProvider<?> delegateProvider)

+        {

+            this.delegateProvider = delegateProvider;

+        }

+

+        public Object loadObject()

+        {

+            return delegateProvider.getObject();

+        }

+    }

+}

diff --git a/src/test/resources/log4j.properties b/cglib/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
similarity index 75%
copy from src/test/resources/log4j.properties
copy to cglib/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
index b981287..1fe9ac3 100644
--- a/src/test/resources/log4j.properties
+++ b/cglib/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
@@ -1,20 +1,17 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You 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.
-#
-log4j.rootLogger=DEBUG, console
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern=%d{MM-dd@HH:mm:ss} %-5p (%c{1}) %3x - %m%n
\ No newline at end of file
+#

+# Licensed to the Apache Software Foundation (ASF) under one or more

+# contributor license agreements.  See the NOTICE file distributed with

+# this work for additional information regarding copyright ownership.

+# The ASF licenses this file to You 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.

+#

+org.apache.commons.proxy2.cglib.CglibProxyFactory
\ No newline at end of file
diff --git a/cglib/src/site/markdown/index.md b/cglib/src/site/markdown/index.md
new file mode 100644
index 0000000..67a8584
--- /dev/null
+++ b/cglib/src/site/markdown/index.md
@@ -0,0 +1,28 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+-->
+
+## Commons Proxy Cglib
+
+Provides the [CglibProxyFactory][] which uses the [cglib][] library
+to create proxy classes. This proxy factory is capable of proxying concrete
+non-`final` types and can thus be considered a *subclassing* proxy factory.
+
+[CglibProxyFactory]: apidocs/org/apache/commons/proxy2/cglib/CglibProxyFactory.html
+
+[cglib]: http://cglib.sourceforge.net/
diff --git a/src/test/java/org/apache/commons/proxy/exception/TestDelegateProviderException.java b/cglib/src/test/java/org/apache/commons/proxy2/cglib/CglibProxyFactoryTest.java
similarity index 81%
copy from src/test/java/org/apache/commons/proxy/exception/TestDelegateProviderException.java
copy to cglib/src/test/java/org/apache/commons/proxy2/cglib/CglibProxyFactoryTest.java
index 45af58c..cb2da8d 100644
--- a/src/test/java/org/apache/commons/proxy/exception/TestDelegateProviderException.java
+++ b/cglib/src/test/java/org/apache/commons/proxy2/cglib/CglibProxyFactoryTest.java
@@ -1,30 +1,31 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.exception;
-
-public class TestDelegateProviderException extends AbstractExceptionClassTestCase
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public TestDelegateProviderException()
-    {
-        super(ObjectProviderException.class);
-    }
-}
\ No newline at end of file
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.cglib;

+

+import org.apache.commons.proxy2.AbstractSubclassingProxyFactoryTestCase;

+

+public class CglibProxyFactoryTest extends AbstractSubclassingProxyFactoryTestCase

+{

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+

+    public CglibProxyFactoryTest()

+    {

+    }

+}

diff --git a/checkstyle.xml b/checkstyle.xml
deleted file mode 100644
index 696d8e6..0000000
--- a/checkstyle.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->     
-<!DOCTYPE module PUBLIC
-    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
-    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
-
-<!-- commons proxy customization of default Checkstyle behavior -->
-<module name="Checker">
-  <property name="localeLanguage" value="en"/>
-
-  <module name="TreeWalker">
-    <!-- Verify that EVERY source file has the appropriate license -->
-    <module name="Header">
-      <property name="headerFile" value="${checkstyle.header.file}"/>
-    </module>
-
-    <!-- no tabs allowed in files -->
-    <module name="TabCharacter"/>
-
-    <!-- check sane import statements -->
-    <module name="AvoidStarImport"/>
-    <module name="RedundantImport"/>
-    <module name="UnusedImports"/>
-
-    <module name="LineLength">
-      <property name="max" value="120"/>
-    </module>
-  </module>
-</module>
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..eb360da
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>commons-proxy2-parent</artifactId>
+        <groupId>org.apache.commons</groupId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>commons-proxy2</artifactId>
+    <name>Commons Proxy Core</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jmock</groupId>
+            <artifactId>jmock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/core/src/main/java/org/apache/commons/proxy2/DefaultProxyFactory.java b/core/src/main/java/org/apache/commons/proxy2/DefaultProxyFactory.java
new file mode 100644
index 0000000..0a6db83
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/DefaultProxyFactory.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;
+
+import java.util.Arrays;
+import java.util.ServiceLoader;
+
+/**
+ * {@link ProxyFactory} implementation that delegates to the first discovered
+ * {@link ProxyFactory} service provider that {@link #canProxy(Class...)}.
+ *
+ * @author Matt Benson
+ */
+class DefaultProxyFactory implements ProxyFactory {
+    /** Shared instance */
+    static final DefaultProxyFactory INSTANCE = new DefaultProxyFactory();
+
+    private static final ServiceLoader<ProxyFactory> SERVICES = ServiceLoader
+            .load(ProxyFactory.class);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean canProxy(Class<?>... proxyClasses) {
+        for (ProxyFactory proxyFactory : SERVICES) {
+            if (proxyFactory.canProxy(proxyClasses)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createDelegatorProxy(ObjectProvider<?> delegateProvider,
+            Class<?>... proxyClasses) {
+        @SuppressWarnings("unchecked")
+        final T result = (T) getCapableProxyFactory(proxyClasses).createDelegatorProxy(
+                delegateProvider, proxyClasses);
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createDelegatorProxy(ClassLoader classLoader,
+            ObjectProvider<?> delegateProvider, Class<?>... proxyClasses) {
+        @SuppressWarnings("unchecked")
+        final T result = (T) getCapableProxyFactory(proxyClasses).createDelegatorProxy(
+                classLoader, delegateProvider, proxyClasses);
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createInterceptorProxy(Object target, Interceptor interceptor,
+            Class<?>... proxyClasses) {
+        @SuppressWarnings("unchecked")
+        final T result = (T) getCapableProxyFactory(proxyClasses).createInterceptorProxy(
+                target, interceptor, proxyClasses);
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createInterceptorProxy(ClassLoader classLoader, Object target,
+            Interceptor interceptor, Class<?>... proxyClasses) {
+        @SuppressWarnings("unchecked")
+        final T result = (T) getCapableProxyFactory(proxyClasses).createInterceptorProxy(
+                classLoader, target, interceptor, proxyClasses);
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createInvokerProxy(Invoker invoker, Class<?>... proxyClasses) {
+        @SuppressWarnings("unchecked")
+        final T result = (T) getCapableProxyFactory(proxyClasses).createInvokerProxy(
+                invoker, proxyClasses);
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker,
+            Class<?>... proxyClasses) {
+        @SuppressWarnings("unchecked")
+        final T result = (T) getCapableProxyFactory(proxyClasses).createInvokerProxy(
+                classLoader, invoker, proxyClasses);
+        return result;
+    }
+
+    private ProxyFactory getCapableProxyFactory(Class<?>... proxyClasses) {
+        for (ProxyFactory proxyFactory : SERVICES) {
+            if (proxyFactory.canProxy(proxyClasses)) {
+                return proxyFactory;
+            }
+        }
+        throw new IllegalArgumentException("Could not proxy "
+                + Arrays.toString(proxyClasses));
+    }
+}
diff --git a/src/main/java/org/apache/commons/proxy/Interceptor.java b/core/src/main/java/org/apache/commons/proxy2/Interceptor.java
similarity index 82%
rename from src/main/java/org/apache/commons/proxy/Interceptor.java
rename to core/src/main/java/org/apache/commons/proxy2/Interceptor.java
index ecb4fe0..5b2c668 100644
--- a/src/main/java/org/apache/commons/proxy/Interceptor.java
+++ b/core/src/main/java/org/apache/commons/proxy2/Interceptor.java
@@ -1,35 +1,41 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy;
-
-import java.io.Serializable;
-
-/**
- * "Intercepts" a method invocation.
- *
- * @author James Carman
- * @since 1.0
- */
-public interface Interceptor extends Serializable
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public Object intercept( Invocation invocation ) throws Throwable;
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+import java.io.Serializable;

+

+/**

+ * "Intercepts" a method invocation.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public interface Interceptor extends Serializable

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    /**

+     * Intercept the specified {@link Invocation}.

+     * @param invocation

+     * @return return value of the method

+     * @throws Throwable

+     */

+    Object intercept( Invocation invocation ) throws Throwable;

+}

diff --git a/src/main/java/org/apache/commons/proxy/Invocation.java b/core/src/main/java/org/apache/commons/proxy2/Invocation.java
similarity index 89%
rename from src/main/java/org/apache/commons/proxy/Invocation.java
rename to core/src/main/java/org/apache/commons/proxy2/Invocation.java
index 571790e..0c0c60b 100644
--- a/src/main/java/org/apache/commons/proxy/Invocation.java
+++ b/core/src/main/java/org/apache/commons/proxy2/Invocation.java
@@ -1,63 +1,63 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy;
-
-import java.lang.reflect.Method;
-
-/**
- * Represents a method invocation.
- *
- * @author James Carman
- * @since 1.0
- */
-public interface Invocation
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns the arguments being passed to this method invocation.  Changes in the elements of this array will be
-     * propagated to the recipient of this invocation.
-     *
-     * @return the arguments being passed to this method invocation
-     */
-    public Object[] getArguments();
-
-    /**
-     * Returns the method being called.
-     *
-     * @return the method being called
-     */
-    public Method getMethod();
-
-    /**
-     * Returns the proxy object on which this invocation was invoked.
-     *
-     * @return the proxy object on which this invocation was invoked
-     */
-    public Object getProxy();
-
-    /**
-     * Called in order to let the invocation proceed.
-     *
-     * @return the return value of the invocation
-     * @throws Throwable any exception or error that was thrown as a result of this invocation
-     */
-    public Object proceed() throws Throwable;
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+import java.lang.reflect.Method;

+

+/**

+ * Method invocation for use by an {@link Interceptor}.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public interface Invocation

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    /**

+     * Returns the arguments being passed to this method invocation.  Changes in the elements of this array will be

+     * propagated to the recipient of this invocation.

+     *

+     * @return the arguments being passed to this method invocation

+     */

+    Object[] getArguments();

+

+    /**

+     * Returns the method being called.

+     *

+     * @return the method being called

+     */

+    Method getMethod();

+

+    /**

+     * Returns the proxy object on which this invocation was invoked.

+     *

+     * @return the proxy object on which this invocation was invoked

+     */

+    Object getProxy();

+

+    /**

+     * Called in order to let the invocation proceed.

+     *

+     * @return the return value of the invocation

+     * @throws Throwable any exception or error that was thrown as a result of this invocation

+     */

+    Object proceed() throws Throwable;

+}

diff --git a/src/main/java/org/apache/commons/proxy/Invoker.java b/core/src/main/java/org/apache/commons/proxy2/Invoker.java
similarity index 87%
rename from src/main/java/org/apache/commons/proxy/Invoker.java
rename to core/src/main/java/org/apache/commons/proxy2/Invoker.java
index ba90110..aa5689c 100644
--- a/src/main/java/org/apache/commons/proxy/Invoker.java
+++ b/core/src/main/java/org/apache/commons/proxy2/Invoker.java
@@ -1,46 +1,46 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy;
-
-import java.io.Serializable;
-import java.lang.reflect.Method;
-
-/**
- * An invoker is responsible for handling a method invocation.
- *
- * @author James Carman
- * @since 1.0
- */
-public interface Invoker extends Serializable
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    /**
-     * "Invokes" the method.  Implementation should throw a {@link org.apache.commons.proxy.exception.InvokerException}
-     * if problems arise while trying to invoke the method.
-     *
-     * @param proxy     the proxy object
-     * @param method    the method being invoked
-     * @param arguments the arguments
-     * @return the return value
-     * @throws Throwable thrown by the implementation
-     */
-    public Object invoke( Object proxy, Method method, Object[] arguments ) throws Throwable;
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+import java.io.Serializable;

+import java.lang.reflect.Method;

+

+/**

+ * An invoker is responsible for handling a method invocation.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public interface Invoker extends Serializable

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    /**

+     * "Invokes" the method.  Implementation should throw a {@link org.apache.commons.proxy2.exception.InvokerException}

+     * if problems arise while trying to invoke the method.

+     *

+     * @param proxy     the proxy2 object

+     * @param method    the method being invoked

+     * @param arguments the arguments

+     * @return the return value

+     * @throws Throwable thrown by the implementation

+     */

+    Object invoke( Object proxy, Method method, Object[] arguments ) throws Throwable;

+}

diff --git a/src/main/java/org/apache/commons/proxy/ObjectProvider.java b/core/src/main/java/org/apache/commons/proxy2/ObjectProvider.java
similarity index 86%
rename from src/main/java/org/apache/commons/proxy/ObjectProvider.java
rename to core/src/main/java/org/apache/commons/proxy2/ObjectProvider.java
index 0581ee3..ea3ce01 100644
--- a/src/main/java/org/apache/commons/proxy/ObjectProvider.java
+++ b/core/src/main/java/org/apache/commons/proxy2/ObjectProvider.java
@@ -1,41 +1,40 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy;
-
-/**
- * Provides an object to a delegating proxy.
- *
- * @author James Carman
- * @since 1.0
- */
-public interface ObjectProvider
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns an object.  Implementing classes should throw a
-     * {@link org.apache.commons.proxy.exception.ObjectProviderException} if any problems arise while
-     * constructing/finding the object.
-     *
-     * @return the object on which the method should be called
-     */
-    public Object getObject();
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+/**

+ * Provides an object to a delegating proxy.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public interface ObjectProvider<T>

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    /**

+     * Returns an object.  Implementing classes should throw a

+     * {@link org.apache.commons.proxy2.exception.ObjectProviderException} if any problems arise while

+     * constructing/finding the object.

+     *

+     * @return the object on which the method should be called

+     */

+    T getObject();

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/ProxyFactory.java b/core/src/main/java/org/apache/commons/proxy2/ProxyFactory.java
new file mode 100644
index 0000000..a3e3ea8
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/ProxyFactory.java
@@ -0,0 +1,106 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+/**

+ * ProxyFactory interface.

+ * @since 2.0

+ */

+public interface ProxyFactory

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    /**

+     * Learn whether this {@link ProxyFactory} is capable of creating a proxy for the specified set of classes.

+     *

+     * @param proxyClasses the proxy2 classes

+     * @return boolean

+     */

+    boolean canProxy( Class<?>... proxyClasses );

+

+    /**

+     * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.  The proxy will be

+     * generated using the current thread's "context class loader."

+     *

+     * @param delegateProvider the delegate provider

+     * @param proxyClasses     the interfaces that the proxy should implement

+     * @return a proxy which delegates to the object provided by the target object provider

+     */

+    <T> T createDelegatorProxy( ObjectProvider<?> delegateProvider, Class<?>... proxyClasses );

+

+    /**

+     * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.

+     *

+     * @param classLoader      the class loader to use when generating the proxy

+     * @param delegateProvider the delegate provider

+     * @param proxyClasses     the interfaces that the proxy should implement

+     * @return a proxy which delegates to the object provided by the target <code>delegateProvider>

+     */

+    <T> T createDelegatorProxy( ClassLoader classLoader, ObjectProvider<?> delegateProvider,

+                                        Class<?>... proxyClasses );

+

+    /**

+     * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the

+     * <code>target</code> object.  The proxy will be generated using the current thread's "context class loader."

+     *

+     * @param target       the target object

+     * @param interceptor  the method interceptor

+     * @param proxyClasses the interfaces that the proxy should implement

+     * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the

+     *         <code>target</code> object.

+     */

+    <T> T createInterceptorProxy( Object target, Interceptor interceptor,

+                                          Class<?>... proxyClasses );

+

+    /**

+     * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the

+     * <code>target</code> object.

+     *

+     * @param classLoader  the class loader to use when generating the proxy

+     * @param target       the target object

+     * @param interceptor  the method interceptor

+     * @param proxyClasses the interfaces that the proxy should implement.

+     * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the

+     *         <code>target</code> object.

+     */

+    <T> T createInterceptorProxy( ClassLoader classLoader, Object target, Interceptor interceptor,

+                                          Class<?>... proxyClasses );

+

+    /**

+     * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.  The proxy will be

+     * generated using the current thread's "context class loader."

+     *

+     * @param invoker      the invoker

+     * @param proxyClasses the interfaces that the proxy should implement

+     * @return a proxy which uses the provided {@link Invoker} to handle all method invocations

+     */

+    <T> T createInvokerProxy( Invoker invoker, Class<?>... proxyClasses );

+

+    /**

+     * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.

+     *

+     * @param classLoader  the class loader to use when generating the proxy

+     * @param invoker      the invoker

+     * @param proxyClasses the interfaces that the proxy should implement

+     * @return a proxy which uses the provided {@link Invoker} to handle all method invocations

+     */

+    <T> T createInvokerProxy( ClassLoader classLoader, Invoker invoker,

+                                      Class<?>... proxyClasses );

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/ProxyUtils.java b/core/src/main/java/org/apache/commons/proxy2/ProxyUtils.java
new file mode 100644
index 0000000..dddab95
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/ProxyUtils.java
@@ -0,0 +1,192 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+import java.lang.reflect.Method;

+import java.util.HashMap;

+import java.util.LinkedList;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * Provides some helpful proxy utility methods.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public final class ProxyUtils

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    public static final Object[] EMPTY_ARGUMENTS = new Object[0];

+    public static final Class<?>[] EMPTY_ARGUMENT_TYPES = new Class[0];

+    private static final Map<Class<?>, Class<?>> WRAPPER_CLASS_MAP = new HashMap<Class<?>, Class<?>>();

+    private static final Map<Class<?>, Object> NULL_VALUE_MAP = new HashMap<Class<?>, Object>();

+

+//**********************************************************************************************************************

+// Static Methods

+//**********************************************************************************************************************

+

+    static

+    {

+        WRAPPER_CLASS_MAP.put(Integer.TYPE, Integer.class);

+        WRAPPER_CLASS_MAP.put(Character.TYPE, Character.class);

+        WRAPPER_CLASS_MAP.put(Boolean.TYPE, Boolean.class);

+        WRAPPER_CLASS_MAP.put(Short.TYPE, Short.class);

+        WRAPPER_CLASS_MAP.put(Long.TYPE, Long.class);

+        WRAPPER_CLASS_MAP.put(Float.TYPE, Float.class);

+        WRAPPER_CLASS_MAP.put(Double.TYPE, Double.class);

+        WRAPPER_CLASS_MAP.put(Byte.TYPE, Byte.class);

+    }

+

+    static

+    {

+        NULL_VALUE_MAP.put(Integer.TYPE, 0);

+        NULL_VALUE_MAP.put(Long.TYPE, (long) 0);

+        NULL_VALUE_MAP.put(Short.TYPE, (short) 0);

+        NULL_VALUE_MAP.put(Byte.TYPE, (byte) 0);

+        NULL_VALUE_MAP.put(Float.TYPE, 0.0f);

+        NULL_VALUE_MAP.put(Double.TYPE, 0.0);

+        NULL_VALUE_MAP.put(Character.TYPE, (char) 0);

+        NULL_VALUE_MAP.put(Boolean.TYPE, Boolean.FALSE);

+    }

+

+    /**

+     * <p>Gets an array of {@link Class} objects representing all interfaces implemented by the given class and its

+     * superclasses.</p>

+     * <p/>

+     * <p>The order is determined by looking through each interface in turn as declared in the source file and following

+     * its hierarchy up. Then each superclass is considered in the same way. Later duplicates are ignored, so the order

+     * is maintained.</p>

+     * <p/>

+     * <b>Note</b>: Implementation of this method was "borrowed" from

+     * <a href="http://commons.apache.org/lang/">Apache Commons Lang</a> to avoid a dependency.</p>

+     *

+     * @param cls the class to look up, may be <code>null</code>

+     * @return an array of {@link Class} objects representing all interfaces implemented by the given class and its

+     *         superclasses or <code>null</code> if input class is null.

+     */

+    public static Class<?>[] getAllInterfaces(Class<?> cls)

+    {

+        final List<Class<?>> interfaces = getAllInterfacesImpl(cls, new LinkedList<Class<?>>());

+        return interfaces == null ? null : (Class[]) interfaces.toArray(new Class[interfaces.size()]);

+    }

+

+    private static List<Class<?>> getAllInterfacesImpl(final Class<?> cls, List<Class<?>> list)

+    {

+        if (cls == null)

+        {

+            return null;

+        }

+        Class<?> currentClass = cls;

+        while (currentClass != null)

+        {

+            Class<?>[] interfaces = currentClass.getInterfaces();

+            for (int i = 0; i < interfaces.length; i++)

+            {

+                if (!list.contains(interfaces[i]))

+                {

+                    list.add(interfaces[i]);

+                }

+                getAllInterfacesImpl(interfaces[i], list);

+            }

+            currentClass = currentClass.getSuperclass();

+        }

+        return list;

+    }

+

+    /**

+     * Returns the class name as you would expect to see it in Java code.

+     * <p/>

+     * <b>Examples:</b> <ul> <li>getJavaClassName( Object[].class ) == "Object[]"</li> <li>getJavaClassName(

+     * Object[][].class ) == "Object[][]"</li> <li>getJavaClassName( Integer.TYPE ) == "int"</li> </p>

+     *

+     * @param clazz the class

+     * @return the class' name as you would expect to see it in Java code

+     */

+    public static String getJavaClassName(Class<?> clazz)

+    {

+        if (clazz.isArray())

+        {

+            return getJavaClassName(clazz.getComponentType()) + "[]";

+        }

+        return clazz.getName();

+    }

+

+    /**

+     * Returns the wrapper class for the given primitive type.

+     *

+     * @param primitiveType the primitive type

+     * @return the wrapper class

+     */

+    public static Class<?> getWrapperClass(Class<?> primitiveType)

+    {

+        return WRAPPER_CLASS_MAP.get(primitiveType);

+    }

+

+    /**

+     * Returns the proper "null value" as specified by the Java language.

+     *

+     * @param type the type

+     * @return the null value

+     */

+    @SuppressWarnings("unchecked")

+    public static <T> T nullValue(Class<T> type)

+    {

+        return (T) NULL_VALUE_MAP.get(type);

+    }

+

+    /**

+     * Learn whether the specified method is/overrides {@link Object#equals(Object)}.

+     * @param method to compare

+     * @return <code>true</code> for a method with signature <code>boolean equals(Object)</code>

+     */

+    public static boolean isEqualsMethod(Method method)

+    {

+        return "equals".equals(method.getName()) &&

+                Boolean.TYPE.equals(method.getReturnType()) &&

+                method.getParameterTypes().length == 1 &&

+                Object.class.equals(method.getParameterTypes()[0]);

+    }

+

+    /**

+     * Learn whether the specified method is/overrides {@link Object#hashCode()}.

+     * @param method to compare

+     * @return true for a method with signature <code>int hashCode()</code>

+     */

+    public static boolean isHashCode(Method method)

+    {

+        return "hashCode".equals(method.getName()) &&

+                Integer.TYPE.equals(method.getReturnType()) &&

+                method.getParameterTypes().length == 0;

+    }

+

+    /**

+     * Get a {@link ProxyFactory} that delegates to discoverable {@link ProxyFactory} service providers.

+     * @return {@link ProxyFactory}

+     */

+    public static ProxyFactory proxyFactory() {

+        return DefaultProxyFactory.INSTANCE;

+    }

+

+    private ProxyUtils() {

+        // Hiding constructor in utility class!

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/exception/InvokerException.java b/core/src/main/java/org/apache/commons/proxy2/exception/InvokerException.java
new file mode 100644
index 0000000..a747968
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/exception/InvokerException.java
@@ -0,0 +1,69 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.exception;

+

+/**

+ * To be used by an {@link org.apache.commons.proxy2.Invoker} when they encounter an error.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class InvokerException extends RuntimeException

+{

+    /** Serialization version */

+    private static final long serialVersionUID = -1L;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new InvokerException instance.

+     */

+    public InvokerException()

+    {

+    }

+

+    /**

+     * Create a new InvokerException instance.

+     * @param message

+     */

+    public InvokerException( String message )

+    {

+        super(message);

+    }

+

+    /**

+     * Create a new InvokerException instance.

+     * @param cause

+     */

+    public InvokerException( Throwable cause )

+    {

+        super(cause);

+    }

+

+    /**

+     * Create a new InvokerException instance.

+     * @param message

+     * @param cause

+     */

+    public InvokerException( String message, Throwable cause )

+    {

+        super(message, cause);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/exception/ObjectProviderException.java b/core/src/main/java/org/apache/commons/proxy2/exception/ObjectProviderException.java
new file mode 100644
index 0000000..7b956ca
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/exception/ObjectProviderException.java
@@ -0,0 +1,70 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.exception;

+

+/**

+ * {@link org.apache.commons.proxy2.ObjectProvider} implementations should throw this exception type to indicate that

+ * there was a problem creating/finding the object.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class ObjectProviderException extends RuntimeException

+{

+    /** Serialization version */

+    private static final long serialVersionUID = -1L;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new ObjectProviderException instance.

+     */

+    public ObjectProviderException()

+    {

+    }

+

+    /**

+     * Create a new ObjectProviderException instance.

+     * @param message

+     */

+    public ObjectProviderException( String message )

+    {

+        super(message);

+    }

+

+    /**

+     * Create a new ObjectProviderException instance.

+     * @param cause

+     */

+    public ObjectProviderException( Throwable cause )

+    {

+        super(cause);

+    }

+

+    /**

+     * Create a new ObjectProviderException instance.

+     * @param message

+     * @param cause

+     */

+    public ObjectProviderException( String message, Throwable cause )

+    {

+        super(message, cause);

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/exception/ProxyFactoryException.java b/core/src/main/java/org/apache/commons/proxy2/exception/ProxyFactoryException.java
similarity index 62%
rename from src/main/java/org/apache/commons/proxy/exception/ProxyFactoryException.java
rename to core/src/main/java/org/apache/commons/proxy2/exception/ProxyFactoryException.java
index 073d2f4..de673ad 100644
--- a/src/main/java/org/apache/commons/proxy/exception/ProxyFactoryException.java
+++ b/core/src/main/java/org/apache/commons/proxy2/exception/ProxyFactoryException.java
@@ -1,52 +1,70 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.exception;
-
-/**
- * A runtime exception type to be used by {@link org.apache.commons.proxy.ProxyFactory proxy factories} when a problem
- * occurs.
- *
- * @author James Carman
- * @since 1.0
- */
-public class ProxyFactoryException extends RuntimeException
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public ProxyFactoryException()
-    {
-    }
-
-    public ProxyFactoryException( String message )
-    {
-        super(message);
-    }
-
-    public ProxyFactoryException( Throwable cause )
-    {
-        super(cause);
-    }
-
-    public ProxyFactoryException( String message, Throwable cause )
-    {
-        super(message, cause);
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.exception;

+

+/**

+ * A runtime exception type to be used by {@link org.apache.commons.proxy2.ProxyFactory proxy factories} when a problem

+ * occurs.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class ProxyFactoryException extends RuntimeException

+{

+    /** Serialization version */

+    private static final long serialVersionUID = -1L;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new ProxyFactoryException instance.

+     */

+    public ProxyFactoryException()

+    {

+    }

+

+    /**

+     * Create a new ProxyFactoryException instance.

+     * @param message

+     */

+    public ProxyFactoryException( String message )

+    {

+        super(message);

+    }

+

+    /**

+     * Create a new ProxyFactoryException instance.

+     * @param cause

+     */

+    public ProxyFactoryException( Throwable cause )

+    {

+        super(cause);

+    }

+

+    /**

+     * Create a new ProxyFactoryException instance.

+     * @param message

+     * @param cause

+     */

+    public ProxyFactoryException( String message, Throwable cause )

+    {

+        super(message, cause);

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/factory/util/AbstractProxyClassGenerator.java b/core/src/main/java/org/apache/commons/proxy2/impl/AbstractProxyClassGenerator.java
similarity index 77%
rename from src/main/java/org/apache/commons/proxy/factory/util/AbstractProxyClassGenerator.java
rename to core/src/main/java/org/apache/commons/proxy2/impl/AbstractProxyClassGenerator.java
index 99ed300..aa777d2 100644
--- a/src/main/java/org/apache/commons/proxy/factory/util/AbstractProxyClassGenerator.java
+++ b/core/src/main/java/org/apache/commons/proxy2/impl/AbstractProxyClassGenerator.java
@@ -1,79 +1,71 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.util;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A useful superclass for {@link ProxyClassGenerator} implementations.
- *
- * @author James Carman
- * @since 1.0
- */
-public abstract class AbstractProxyClassGenerator implements ProxyClassGenerator
-{
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns all methods that a proxy class must implement from the proxy interfaces.  This method makes sure there
-     * are no method signature clashes. For methods with the same signature (name and parameter types), the one
-     * encountered first will be returned in the result. Final methods are also excluded from the result.
-     *
-     * @param proxyClasses the interfaces the proxy class must implement
-     * @return all methods that the proxy class must implement
-     */
-    public static Method[] getImplementationMethods( Class[] proxyClasses )
-    {
-        final Map signatureMethodMap = new HashMap();
-        final Set finalizedSignatures = new HashSet();
-        for( int i = 0; i < proxyClasses.length; i++ )
-        {
-            Class proxyInterface = proxyClasses[i];
-            final Method[] methods = proxyInterface.getMethods();
-            for( int j = 0; j < methods.length; j++ )
-            {
-                final MethodSignature signature = new MethodSignature(methods[j]);
-                if( Modifier.isFinal(methods[j].getModifiers()) )
-                {
-                    finalizedSignatures.add(signature);
-                }
-                else if( !signatureMethodMap.containsKey(signature) )
-                {
-                    signatureMethodMap.put(signature, methods[j]);
-                }
-            }
-        }
-        final Collection resultingMethods = signatureMethodMap.values();
-        for( Iterator i = finalizedSignatures.iterator(); i.hasNext(); )
-        {
-            MethodSignature signature = ( MethodSignature ) i.next();
-            resultingMethods.remove(signatureMethodMap.get(signature));
-        }
-        return ( Method[] ) resultingMethods.toArray(new Method[resultingMethods.size()]);
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.impl;

+

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.*;

+

+/**

+ * A useful superclass for {@link ProxyClassGenerator} implementations.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public abstract class AbstractProxyClassGenerator implements ProxyClassGenerator

+{

+//**********************************************************************************************************************

+// Static Methods

+//**********************************************************************************************************************

+

+    /**

+     * Returns all methods that a proxy class must implement from the proxy interfaces.  This method makes sure there

+     * are no method signature clashes. For methods with the same signature (name and parameter types), the one

+     * encountered first will be returned in the result. Final methods are also excluded from the result.

+     *

+     * @param proxyClasses the interfaces the proxy class must implement

+     * @return all methods that the proxy class must implement

+     */

+    public static Method[] getImplementationMethods( Class<?>[] proxyClasses )

+    {

+        final Map<MethodSignature, Method> signatureMethodMap = new HashMap<MethodSignature, Method>();

+        final Set<MethodSignature> finalizedSignatures = new HashSet<MethodSignature>();

+        for( int i = 0; i < proxyClasses.length; i++ )

+        {

+            Class<?> proxyInterface = proxyClasses[i];

+            final Method[] methods = proxyInterface.getMethods();

+            for( int j = 0; j < methods.length; j++ )

+            {

+                final MethodSignature signature = new MethodSignature(methods[j]);

+                if( Modifier.isFinal(methods[j].getModifiers()) )

+                {

+                    finalizedSignatures.add(signature);

+                }

+                else if( !signatureMethodMap.containsKey(signature) )

+                {

+                    signatureMethodMap.put(signature, methods[j]);

+                }

+            }

+        }

+        final Collection<Method> resultingMethods = signatureMethodMap.values();

+        for (MethodSignature signature : finalizedSignatures) {

+            resultingMethods.remove(signatureMethodMap.get(signature));

+        }

+        return resultingMethods.toArray(new Method[resultingMethods.size()]);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/impl/AbstractProxyFactory.java b/core/src/main/java/org/apache/commons/proxy2/impl/AbstractProxyFactory.java
new file mode 100644
index 0000000..253d98c
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/impl/AbstractProxyFactory.java
@@ -0,0 +1,94 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.impl;

+

+import org.apache.commons.proxy2.Interceptor;

+import org.apache.commons.proxy2.Invoker;

+import org.apache.commons.proxy2.ObjectProvider;

+import org.apache.commons.proxy2.ProxyFactory;

+

+/**

+ * Base abstract {@link ProxyFactory} implementation, primarily providing

+ * implementations of the interface methods that are typically convenience

+ * constructs over the other methods.

+ */

+public abstract class AbstractProxyFactory implements ProxyFactory

+{

+    /**

+     * Returns true if all <code>proxyClasses</code> are interfaces.

+     *

+     * @param proxyClasses the proxy classes

+     * @return true if all <code>proxyClasses</code> are interfaces

+     */

+    public boolean canProxy( Class<?>... proxyClasses )

+    {

+        for( Class<?> proxyClass : proxyClasses )

+        {

+            if( !proxyClass.isInterface() )

+            {

+                return false;

+            }

+        }

+        return true;

+    }

+

+    /**

+     * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.  The proxy will be

+     * generated using the current thread's "context class loader."

+     *

+     * @param delegateProvider the delegate provider

+     * @param proxyClasses     the interfaces that the proxy should implement

+     * @return a proxy which delegates to the object provided by the target object provider

+     */

+    public <T> T createDelegatorProxy( ObjectProvider<?> delegateProvider, Class<?>... proxyClasses )

+    {

+        return createDelegatorProxy(Thread.currentThread().getContextClassLoader(), delegateProvider, proxyClasses);

+    }

+

+    /**

+     * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the

+     * <code>target</code> object.  The proxy will be generated using the current thread's "context class loader."

+     *

+     * @param target       the target object

+     * @param interceptor  the method interceptor

+     * @param proxyClasses the interfaces that the proxy should implement

+     * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the

+     *         <code>target</code> object.

+     */

+    public <T> T createInterceptorProxy( Object target, Interceptor interceptor,

+                                          Class<?>... proxyClasses )

+    {

+        return createInterceptorProxy(Thread.currentThread().getContextClassLoader(), target, interceptor,

+                                      proxyClasses);

+    }

+

+    /**

+     * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.  The proxy will be

+     * generated using the current thread's "context class loader."

+     *

+     * @param invoker      the invoker

+     * @param proxyClasses the interfaces that the proxy should implement

+     * @return a proxy which uses the provided {@link Invoker} to handle all method invocations

+     */

+    public <T> T createInvokerProxy( Invoker invoker, Class<?>... proxyClasses )

+    {

+        return createInvokerProxy(Thread.currentThread().getContextClassLoader(), invoker,

+                                  proxyClasses);

+    }

+

+}

diff --git a/src/main/java/org/apache/commons/proxy/factory/util/AbstractSubclassingProxyFactory.java b/core/src/main/java/org/apache/commons/proxy2/impl/AbstractSubclassingProxyFactory.java
similarity index 61%
rename from src/main/java/org/apache/commons/proxy/factory/util/AbstractSubclassingProxyFactory.java
rename to core/src/main/java/org/apache/commons/proxy2/impl/AbstractSubclassingProxyFactory.java
index 99df972..b87499b 100644
--- a/src/main/java/org/apache/commons/proxy/factory/util/AbstractSubclassingProxyFactory.java
+++ b/core/src/main/java/org/apache/commons/proxy2/impl/AbstractSubclassingProxyFactory.java
@@ -1,169 +1,145 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.util;
-
-import org.apache.commons.proxy.ProxyFactory;
-import org.apache.commons.proxy.exception.ProxyFactoryException;
-
-import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * A useful superclass for a {@link ProxyFactory} which supports subclassing rather than merely implementing interfaces.
- *
- * @author James Carman
- * @since 1.0
- */
-public abstract class AbstractSubclassingProxyFactory extends ProxyFactory
-{
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
-     * class from <code>proxyClasses</code>.
-     *
-     * @param proxyClasses the proxy classes
-     * @return either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
-     *         class from <code>proxyClasses</code>
-     * @throws ProxyFactoryException if multiple non-interface classes are contained in <code>proxyClasses</code> or any
-     *                               of the non-interface classes are final
-     */
-    public static Class getSuperclass( Class[] proxyClasses )
-    {
-        final Class[] superclasses = toNonInterfaces(proxyClasses);
-        switch( superclasses.length )
-        {
-            case 0:
-                return Object.class;
-            case 1:
-                final Class superclass = superclasses[0];
-                if( Modifier.isFinal(superclass.getModifiers()) )
-                {
-                    throw new ProxyFactoryException(
-                            "Proxy class cannot extend " + superclass.getName() + " as it is final.");
-                }
-                if( !hasSuitableDefaultConstructor(superclass) )
-                {
-                    throw new ProxyFactoryException("Proxy class cannot extend " + superclass.getName() +
-                            ", because it has no visible \"default\" constructor.");
-                }
-                return superclass;
-            default:
-                final StringBuffer errorMessage = new StringBuffer("Proxy class cannot extend ");
-                for( int i = 0; i < superclasses.length; i++ )
-                {
-                    Class c = superclasses[i];
-                    errorMessage.append(c.getName());
-                    if( i != superclasses.length - 1 )
-                    {
-                        errorMessage.append(", ");
-                    }
-                }
-                errorMessage.append("; multiple inheritance not allowed.");
-                throw new ProxyFactoryException(errorMessage.toString());
-        }
-    }
-
-    private static boolean hasSuitableDefaultConstructor( Class superclass )
-    {
-        final Constructor[] declaredConstructors = superclass.getDeclaredConstructors();
-        for( int i = 0; i < declaredConstructors.length; i++ )
-        {
-            Constructor constructor = declaredConstructors[i];
-            if( constructor.getParameterTypes().length == 0 && ( Modifier.isPublic(constructor.getModifiers()) ||
-                    Modifier.isProtected(constructor.getModifiers()) ) )
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns the <code>proxyClasses</code> transformed into an array of only the interface classes.
-     * <p/>
-     * <b>Note</b>: This class will append {@link Serializable} to the end of the list if it's
-     * not found!
-     *
-     * @param proxyClasses the proxy classes
-     * @return the <code>proxyClasses</code> transformed into an array of only the interface classes
-     */
-    protected static Class[] toInterfaces( Class[] proxyClasses )
-    {
-        final Collection interfaces = new LinkedList();
-        boolean serializableFound = false;
-        for( int i = 0; i < proxyClasses.length; i++ )
-        {
-            Class proxyInterface = proxyClasses[i];
-            if( proxyInterface.isInterface() )
-            {
-                interfaces.add(proxyInterface);
-            }
-            serializableFound |= ( Serializable.class.equals(proxyInterface) );
-        }
-        if( !serializableFound )
-        {
-            interfaces.add(Serializable.class);
-        }
-        return ( Class[] ) interfaces.toArray(new Class[interfaces.size()]);
-    }
-
-    private static Class[] toNonInterfaces( Class[] proxyClasses )
-    {
-        final List superclasses = new LinkedList();
-        for( int i = 0; i < proxyClasses.length; i++ )
-        {
-            Class proxyClass = proxyClasses[i];
-            if( !proxyClass.isInterface() )
-            {
-                superclasses.add(proxyClass);
-            }
-        }
-        return ( Class[] ) superclasses.toArray(new Class[superclasses.size()]);
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns true if a suitable superclass can be found, given the desired <code>proxyClasses</code>.
-     *
-     * @param proxyClasses the proxy classes
-     * @return true if a suitable superclass can be found, given the desired <code>proxyClasses</code>
-     */
-    public boolean canProxy( Class[] proxyClasses )
-    {
-        try
-        {
-            getSuperclass(proxyClasses);
-            return true;
-        }
-        catch( ProxyFactoryException e )
-        {
-            return false;
-        }
-    }
-}
-
+package org.apache.commons.proxy2.impl;

+

+import org.apache.commons.proxy2.exception.ProxyFactoryException;

+

+import java.io.Serializable;

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Modifier;

+import java.util.LinkedHashSet;

+import java.util.Set;

+

+/**

+ * Parent {@link AbstractProxyFactory} for implementations that permit the generation of

+ * proxies with a specific inheritance hierarchy.

+ */

+public abstract class AbstractSubclassingProxyFactory extends AbstractProxyFactory

+{

+//**********************************************************************************************************************

+// ProxyFactory Implementation

+//**********************************************************************************************************************

+

+    /**

+     * Returns true if a suitable superclass can be found, given the desired <code>proxyClasses</code>.

+     *

+     * @param proxyClasses the proxy classes

+     * @return true if a suitable superclass can be found, given the desired <code>proxyClasses</code>

+     */

+    public boolean canProxy( Class<?>... proxyClasses )

+    {

+        try

+        {

+            getSuperclass(proxyClasses);

+            return true;

+        }

+        catch( ProxyFactoryException e )

+        {

+            return false;

+        }

+    }

+

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    private static boolean hasSuitableDefaultConstructor( Class<?> superclass )

+    {

+        final Constructor<?>[] declaredConstructors = superclass.getDeclaredConstructors();

+        for( int i = 0; i < declaredConstructors.length; i++ )

+        {

+            Constructor<?> constructor = declaredConstructors[i];

+            if( constructor.getParameterTypes().length == 0 && ( Modifier.isPublic(constructor.getModifiers()) ||

+                    Modifier.isProtected(constructor.getModifiers()) ) )

+            {

+                return true;

+            }

+        }

+        return false;

+    }

+

+    private static Class<?>[] toNonInterfaces( Class<?>[] proxyClasses )

+    {

+        final Set<Class<?>> superclasses = new LinkedHashSet<Class<?>>();

+        for (Class<?> proxyClass : proxyClasses)

+        {

+            if( !proxyClass.isInterface())

+            {

+                superclasses.add(proxyClass);

+            }

+        }

+        return superclasses.toArray(new Class[superclasses.size()]);

+    }

+

+    /**

+     * Returns the <code>proxyClasses</code> transformed into an array of only the interface classes.

+     * <p/>

+     * <b>Note</b>: This class will append {@link Serializable} to the end of the list if it's

+     * not found!

+     *

+     * @param proxyClasses the proxy classes

+     * @return the <code>proxyClasses</code> transformed into an array of only the interface classes

+     */

+    protected static Class<?>[] toInterfaces( Class<?>[] proxyClasses )

+    {

+        final Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();

+        boolean serializableFound = false;

+        for (Class<?> proxyClass : proxyClasses)

+        {

+            if( proxyClass.isInterface() )

+            {

+                interfaces.add(proxyClass);

+            }

+            serializableFound |= ( Serializable.class.equals(proxyClass) );

+        }

+        if( !serializableFound )

+        {

+            interfaces.add(Serializable.class);

+        }

+        return ( Class[] ) interfaces.toArray(new Class[interfaces.size()]);

+    }

+

+    /**

+     * Returns either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface

+     * class from <code>proxyClasses</code>.

+     *

+     * @param proxyClasses the proxy classes

+     * @return either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface

+     *         class from <code>proxyClasses</code>

+     * @throws ProxyFactoryException if multiple non-interface classes are contained in <code>proxyClasses</code> or any

+     *                               of the non-interface classes are final

+     */

+    public static Class<?> getSuperclass( Class<?>[] proxyClasses )

+    {

+        final Class<?>[] superclasses = toNonInterfaces(proxyClasses);

+        switch( superclasses.length )

+        {

+            case 0:

+                return Object.class;

+            case 1:

+                final Class<?> superclass = superclasses[0];

+                if( Modifier.isFinal(superclass.getModifiers()) )

+                {

+                    throw new ProxyFactoryException(

+                            "Proxy class cannot extend " + superclass.getName() + " as it is final.");

+                }

+                if( !hasSuitableDefaultConstructor(superclass) )

+                {

+                    throw new ProxyFactoryException("Proxy class cannot extend " + superclass.getName() +

+                            ", because it has no visible \"default\" constructor.");

+                }

+                return superclass;

+            default:

+                final StringBuilder errorMessage = new StringBuilder("Proxy class cannot extend ");

+                for( int i = 0; i < superclasses.length; i++ )

+                {

+                    Class<?> c = superclasses[i];

+                    errorMessage.append(c.getName());

+                    if( i != superclasses.length - 1 )

+                    {

+                        errorMessage.append(", ");

+                    }

+                }

+                errorMessage.append("; multiple inheritance not allowed.");

+                throw new ProxyFactoryException(errorMessage.toString());

+        }

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/impl/MethodSignature.java b/core/src/main/java/org/apache/commons/proxy2/impl/MethodSignature.java
new file mode 100644
index 0000000..05cad71
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/impl/MethodSignature.java
@@ -0,0 +1,263 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.impl;

+

+import java.io.Serializable;

+import java.lang.reflect.Array;

+import java.lang.reflect.Method;

+import java.text.ParsePosition;

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+import org.apache.commons.lang3.ArrayUtils;

+import org.apache.commons.lang3.StringUtils;

+import org.apache.commons.lang3.Validate;

+import org.apache.commons.lang3.builder.HashCodeBuilder;

+import org.apache.commons.lang3.reflect.MethodUtils;

+import org.apache.commons.lang3.tuple.Pair;

+

+/**

+ * A class for capturing the signature of a method (its name and parameter types).

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class MethodSignature implements Serializable

+{

+    private static final long serialVersionUID = 1L;

+

+    private static final Map<Class<?>, Character> PRIMITIVE_ABBREVIATIONS;

+    private static final Map<Character, Class<?>> REVERSE_ABBREVIATIONS;

+    static

+    {

+        final Map<Class<?>, Character> primitiveAbbreviations = new HashMap<Class<?>, Character>();

+        primitiveAbbreviations.put(Boolean.TYPE, Character.valueOf('Z'));

+        primitiveAbbreviations.put(Byte.TYPE, Character.valueOf('B'));

+        primitiveAbbreviations.put(Short.TYPE, Character.valueOf('S'));

+        primitiveAbbreviations.put(Integer.TYPE, Character.valueOf('I'));

+        primitiveAbbreviations.put(Character.TYPE, Character.valueOf('C'));

+        primitiveAbbreviations.put(Long.TYPE, Character.valueOf('J'));

+        primitiveAbbreviations.put(Float.TYPE, Character.valueOf('F'));

+        primitiveAbbreviations.put(Double.TYPE, Character.valueOf('D'));

+        primitiveAbbreviations.put(Void.TYPE, Character.valueOf('V'));

+        final Map<Character, Class<?>> reverseAbbreviations = new HashMap<Character, Class<?>>();

+        for (Map.Entry<Class<?>, Character> e : primitiveAbbreviations.entrySet())

+        {

+            reverseAbbreviations.put(e.getValue(), e.getKey());

+        }

+        PRIMITIVE_ABBREVIATIONS = Collections.unmodifiableMap(primitiveAbbreviations);

+        REVERSE_ABBREVIATIONS = Collections.unmodifiableMap(reverseAbbreviations);

+    }

+

+    private static void appendTo(StringBuilder buf, Class<?> type)

+    {

+        if (type.isPrimitive())

+        {

+            buf.append(PRIMITIVE_ABBREVIATIONS.get(type));

+        }

+        else if (type.isArray())

+        {

+            buf.append('[');

+            appendTo(buf, type.getComponentType());

+        }

+        else

+        {

+            buf.append('L').append(type.getName().replace('.', '/')).append(';');

+        }

+    }

+

+    private static class SignaturePosition extends ParsePosition

+    {

+        SignaturePosition() {

+            super(0);

+        }

+

+        SignaturePosition next()

+        {

+            return plus(1);

+        }

+

+        SignaturePosition plus(int addend)

+        {

+            setIndex(getIndex() + addend);

+            return this;

+        }

+    }

+

+    private static Pair<String, Class<?>[]> parse(String internal)

+    {

+        Validate.notBlank(internal, "Cannot parse blank method signature");

+        final SignaturePosition pos = new SignaturePosition();

+        int lparen = internal.indexOf('(', pos.getIndex());

+        Validate.isTrue(lparen > 0, "Method signature \"%s\" requires parentheses", internal);

+        final String name = internal.substring(0, lparen).trim();

+        Validate.notBlank(name, "Method signature \"%s\" has blank name", internal);

+

+        pos.setIndex(lparen + 1);

+

+        boolean complete = false;

+        final List<Class<?>> params = new ArrayList<Class<?>>();

+        while (pos.getIndex() < internal.length())

+        {

+            final char c = internal.charAt(pos.getIndex());

+            if (Character.isWhitespace(c)) {

+                pos.next();

+                continue;

+            }

+            final Character k = Character.valueOf(c);

+            if (REVERSE_ABBREVIATIONS.containsKey(k))

+            {

+                params.add(REVERSE_ABBREVIATIONS.get(k));

+                pos.next();

+                continue;

+            }

+            if (')' == c)

+            {

+                complete = true;

+                pos.next();

+                break;

+            }

+            try {

+                params.add(parseType(internal, pos));

+            } catch (ClassNotFoundException e) {

+                throw new IllegalArgumentException(String.format("Method signature \"%s\" references unknown type",

+                    internal), e);

+            }

+        }

+        Validate.isTrue(complete, "Method signature \"%s\" is incomplete", internal);

+        Validate.isTrue(StringUtils.isBlank(internal.substring(pos.getIndex())),

+            "Method signature \"%s\" includes unrecognized content beyond end", internal);

+

+        return Pair.of(name, params.toArray(ArrayUtils.EMPTY_CLASS_ARRAY));

+    }

+

+    private static Class<?> parseType(String internal, SignaturePosition pos) throws ClassNotFoundException {

+        final int here = pos.getIndex();

+        final char c = internal.charAt(here);

+

+        switch (c)

+        {

+        case '[':

+            pos.next();

+            final Class<?> componentType = parseType(internal, pos);

+            return Array.newInstance(componentType, 0).getClass();

+        case 'L':

+            pos.next();

+            final int type = pos.getIndex();

+            final int semi = internal.indexOf(';', type);

+            Validate.isTrue(semi > 0, "Type at index %s of method signature \"%s\" not terminated by semicolon", here,

+                internal);

+            final String className = internal.substring(type, semi).replace('/', '.');

+            Validate.notBlank(className, "Invalid classname at position %s of method signature \"%s\"", type, internal);

+            pos.setIndex(semi + 1);

+            return Class.forName(className);

+        default:

+            throw new IllegalArgumentException(String.format(

+            "Unexpected character at index %s of method signature \"%s\"", here, internal));

+        }

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Fields

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * Stored as a Java method descriptor minus return type.

+     */

+    private final String internal;

+

+//----------------------------------------------------------------------------------------------------------------------

+// Constructors

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * Create a new MethodSignature instance.

+     *

+     * @param method

+     */

+    public MethodSignature(Method method)

+    {

+        final StringBuilder buf = new StringBuilder(method.getName()).append('(');

+        for (Class<?> p : method.getParameterTypes())

+        {

+            appendTo(buf, p);

+        }

+        buf.append(')');

+        this.internal = buf.toString();

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Methods

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * Get the corresponding {@link Method} instance

+     * from the specified {@link Class}.

+     * @param type

+     * @return Method

+     */

+    public Method toMethod(Class<?> type)

+    {

+        final Pair<String,Class<?>[]> info = parse(internal);

+        return MethodUtils.getAccessibleMethod(type, info.getLeft(), info.getRight());

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Canonical Methods

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * {@inheritDoc}

+     */

+    public boolean equals(Object o)

+    {

+        if (o == null)

+        {

+            return false;

+        }

+        if (o == this)

+        {

+            return true;

+        }

+        if (o.getClass() != getClass())

+        {

+            return false;

+        }

+        MethodSignature other = (MethodSignature) o;

+        return other.internal.equals(internal);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public int hashCode()

+    {

+        return new HashCodeBuilder().append(internal).build();

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public String toString()

+    {

+        return internal;

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/factory/util/ProxyClassCache.java b/core/src/main/java/org/apache/commons/proxy2/impl/ProxyClassCache.java
similarity index 64%
rename from src/main/java/org/apache/commons/proxy/factory/util/ProxyClassCache.java
rename to core/src/main/java/org/apache/commons/proxy2/impl/ProxyClassCache.java
index ead88fc..726a046 100644
--- a/src/main/java/org/apache/commons/proxy/factory/util/ProxyClassCache.java
+++ b/core/src/main/java/org/apache/commons/proxy2/impl/ProxyClassCache.java
@@ -1,116 +1,110 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.util;
-
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-/**
- * A cache for storing implementation classes for proxies based on a specific type of {@link ProxyClassGenerator}.  A
- * proxy class cache ensures that there is only one class for every
- * {@link ProxyClassGenerator}/{@link ClassLoader}/proxy class array combination.
- *
- * @author James Carman
- * @since 1.0
- */
-public class ProxyClassCache
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Map loaderToClassCache = new WeakHashMap();
-    private final ProxyClassGenerator proxyClassGenerator;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public ProxyClassCache( ProxyClassGenerator proxyClassGenerator )
-    {
-        this.proxyClassGenerator = proxyClassGenerator;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Map getClassCache( ClassLoader classLoader )
-    {
-        Map cache = ( Map ) loaderToClassCache.get(classLoader);
-        if( cache == null )
-        {
-            cache = new HashMap();
-            loaderToClassCache.put(classLoader, cache);
-        }
-        return cache;
-    }
-
-    private String toClassCacheKey( Class[] proxyClasses )
-    {
-        final StringBuffer sb = new StringBuffer();
-        for( int i = 0; i < proxyClasses.length; i++ )
-        {
-            Class proxyInterface = proxyClasses[i];
-            sb.append(proxyInterface.getName());
-            if( i != proxyClasses.length - 1 )
-            {
-                sb.append(",");
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Returns the proxy class generated by the {@link ProxyClassGenerator} using the specified {@link ClassLoader} and
-     * array of proxy classes.
-     *
-     * @param classLoader  the classloader
-     * @param proxyClasses the proxy classes
-     * @return the proxy class generated by the {@link ProxyClassGenerator} using the specified {@link ClassLoader} and
-     *         array of proxy classes
-     */
-    public synchronized Class getProxyClass( ClassLoader classLoader, Class[] proxyClasses )
-    {
-        final Map classCache = getClassCache(classLoader);
-        final String key = toClassCacheKey(proxyClasses);
-        Class proxyClass;
-        WeakReference proxyClassReference = ( WeakReference ) classCache.get(key);
-        if( proxyClassReference == null )
-        {
-            proxyClass = proxyClassGenerator.generateProxyClass(classLoader, proxyClasses);
-            classCache.put(key, new WeakReference(proxyClass));
-        }
-        else
-        {
-            synchronized( proxyClassReference )
-            {
-                proxyClass = ( Class ) proxyClassReference.get();
-                if( proxyClass == null )
-                {
-                    proxyClass = proxyClassGenerator.generateProxyClass(classLoader, proxyClasses);
-                    classCache.put(key, new WeakReference(proxyClass));
-                }
-            }
-        }
-        return proxyClass;
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.impl;

+

+import java.lang.ref.Reference;

+import java.lang.ref.WeakReference;

+import java.util.*;

+

+/**

+ * A cache for storing implementation classes for proxies based on a specific type of {@link ProxyClassGenerator}.  A

+ * proxy class cache ensures that there is only one class for every

+ * {@link ProxyClassGenerator}/{@link ClassLoader}/proxy class array combination.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class ProxyClassCache

+{

+

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private final Map<ClassLoader, Map<Set<Class<?>>, WeakReference<Class<?>>>> loaderToClassCache

+            = new WeakHashMap<ClassLoader, Map<Set<Class<?>>, WeakReference<Class<?>>>>();

+    private final ProxyClassGenerator proxyClassGenerator;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new ProxyClassCache instance.

+     * @param proxyClassGenerator

+     */

+    public ProxyClassCache( ProxyClassGenerator proxyClassGenerator )

+    {

+        this.proxyClassGenerator = proxyClassGenerator;

+    }

+

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    private Map<Set<Class<?>>, WeakReference<Class<?>>> getClassCache( ClassLoader classLoader )

+    {

+        Map<Set<Class<?>>, WeakReference<Class<?>>> cache = loaderToClassCache.get(classLoader);

+        if( cache == null )

+        {

+            cache = new HashMap<Set<Class<?>>, WeakReference<Class<?>>>();

+            loaderToClassCache.put(classLoader, cache);

+        }

+        return cache;

+    }

+

+    private Set<Class<?>> toClassCacheKey( Class<?>[] proxyClasses )

+    {

+        return new HashSet<Class<?>>(Arrays.asList(proxyClasses));

+    }

+

+    /**

+     * Returns the proxy class generated by the {@link ProxyClassGenerator} using the specified {@link ClassLoader} and

+     * array of proxy classes.

+     *

+     * @param classLoader  the classloader

+     * @param proxyClasses the proxy classes

+     * @return the proxy class generated by the {@link ProxyClassGenerator} using the specified {@link ClassLoader} and

+     *         array of proxy classes

+     */

+    public synchronized Class<?> getProxyClass( ClassLoader classLoader, Class<?>[] proxyClasses )

+    {

+        final Map<Set<Class<?>>, WeakReference<Class<?>>> classCache = getClassCache(classLoader);

+        final Set<Class<?>> key = toClassCacheKey(proxyClasses);

+        Class<?> proxyClass;

+        Reference<Class<?>> proxyClassReference = classCache.get(key);

+        if( proxyClassReference == null )

+        {

+            proxyClass = proxyClassGenerator.generateProxyClass(classLoader, proxyClasses);

+            classCache.put(key, new WeakReference<Class<?>>(proxyClass));

+        }

+        else

+        {

+            synchronized( proxyClassReference )

+            {

+                proxyClass = proxyClassReference.get();

+                if( proxyClass == null )

+                {

+                    proxyClass = proxyClassGenerator.generateProxyClass(classLoader, proxyClasses);

+                    classCache.put(key, new WeakReference<Class<?>>(proxyClass));

+                }

+            }

+        }

+        return proxyClass;

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/factory/util/ProxyClassGenerator.java b/core/src/main/java/org/apache/commons/proxy2/impl/ProxyClassGenerator.java
similarity index 72%
rename from src/main/java/org/apache/commons/proxy/factory/util/ProxyClassGenerator.java
rename to core/src/main/java/org/apache/commons/proxy2/impl/ProxyClassGenerator.java
index effd113..531ca3f 100644
--- a/src/main/java/org/apache/commons/proxy/factory/util/ProxyClassGenerator.java
+++ b/core/src/main/java/org/apache/commons/proxy2/impl/ProxyClassGenerator.java
@@ -1,41 +1,43 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.util;
-
-/**
- * A proxy class generator generates specific type of proxies (interceptor, invoker, etc.).
- *
- * @author James Carman
- * @since 1.0
- */
-public interface ProxyClassGenerator
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    /**
-     * Generates a proxy class for the supplied {@link ClassLoader} and proxy classes.
-     *
-     * @param classLoader  the classloader
-     * @param proxyClasses the proxy classes
-     * @return the dynamically generated proxy class
-     */
-    public Class generateProxyClass( ClassLoader classLoader, Class[] proxyClasses );
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.impl;

+

+/**

+ * A proxy class generator generates Class instances for a given combination of interfaces

+ * in a given classloader.  Typically, in the context of a {@link org.apache.commons.proxy2.ProxyFactory}

+ * implementation, an instance will generate proxy class instances for a specific type of proxies (interceptor,

+ * invoker, etc.) and will be associated with a corresponding {@link ProxyClassCache}.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public interface ProxyClassGenerator

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    /**

+     * Generates a proxy class for the supplied {@link ClassLoader} and proxy classes.

+     *

+     * @param classLoader  the classloader

+     * @param proxyClasses the proxy classes

+     * @return the dynamically generated proxy class

+     */

+    Class<?> generateProxyClass( ClassLoader classLoader, Class<?>... proxyClasses );

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/InterceptorUtils.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/InterceptorUtils.java
new file mode 100644
index 0000000..7751bc1
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/InterceptorUtils.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ObjectProvider;
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+
+public final class InterceptorUtils
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Static Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    /**
+     * Creates an {@link Interceptor} which always returns a constant value (for all methods).
+     * @param value the constant
+     * @return an {@link Interceptor} which always returns a constant value (for all methods)
+     */
+    public static Interceptor constant(Object value)
+    {
+        return new ObjectProviderInterceptor(ObjectProviderUtils.constant(value));
+    }
+
+    /**
+     * Creates an {@link Interceptor} which returns the resulting object from an
+     * object provider (for all methods).
+     * @param provider the object provider
+     * @return an {@link Interceptor} which returns the resulting object from an
+     * object provider (for all methods)
+     */
+    public static Interceptor provider(ObjectProvider<?> provider)
+    {
+        return new ObjectProviderInterceptor(provider);
+    }
+
+    /**
+     * Creates an {@link Interceptor} which throws a specific exception (for all methods).
+     * @param e the exception
+     * @return an {@link Interceptor} which throws a specific exception (for all methods)
+     */
+    public static Interceptor throwing(Exception e)
+    {
+        return new ThrowingInterceptor(ObjectProviderUtils.constant(e));
+    }
+
+    /**
+     * Creates an {@link Interceptor} which throws the exception provided by an object
+     * provider (for all methods).
+     * @param provider the object provider
+     * @return an {@link Interceptor} which throws the exception provided by an object
+     * provider (for all methods)
+     */
+    public static Interceptor throwing(ObjectProvider<? extends Exception> provider)
+    {
+        return new ThrowingInterceptor(provider);
+    }
+
+    /**
+     * Creates an {@link Interceptor} that delegates to the specified {@link Invoker}.
+     * @param invoker delegate
+     * @return invoker {@link Interceptor}
+     */
+    public static Interceptor invoking(Invoker invoker)
+    {
+        return new InvokerInterceptor(invoker);
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    private InterceptorUtils()
+    {
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/InvokerInterceptor.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/InvokerInterceptor.java
new file mode 100644
index 0000000..d4fdaea
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/InvokerInterceptor.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.Invoker;
+
+/**
+ * Adapts an {@link Invoker} to the {@link Interceptor} interface.
+ */
+public class InvokerInterceptor implements Interceptor
+{
+    private static final long serialVersionUID = 1L;
+
+    private final Invoker invoker;
+
+    public InvokerInterceptor(Invoker invoker)
+    {
+        super();
+        this.invoker = Validate.notNull(invoker);
+    }
+
+    @Override
+    public Object intercept(Invocation invocation) throws Throwable
+    {
+        return invoker.invoke(invocation.getProxy(), invocation.getMethod(), invocation.getArguments());
+    }
+
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/ObjectProviderInterceptor.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/ObjectProviderInterceptor.java
new file mode 100644
index 0000000..250aae4
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/ObjectProviderInterceptor.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.ObjectProvider;
+
+/**
+ * A {@link ObjectProviderInterceptor} merely returns the value returned from
+ * {@link org.apache.commons.proxy2.ObjectProvider#getObject()}.
+ */
+public class ObjectProviderInterceptor implements Interceptor
+{
+	private static final long serialVersionUID = 1L;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+	private final ObjectProvider<?> provider;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public ObjectProviderInterceptor(ObjectProvider<?> provider)
+    {
+        this.provider = Validate.notNull(provider, "Provider cannot be null.");
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Interceptor Implementation
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Override
+    public Object intercept(Invocation invocation) throws Throwable
+    {
+        return provider.getObject();
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/SwitchInterceptor.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/SwitchInterceptor.java
new file mode 100644
index 0000000..a06b1d2
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/SwitchInterceptor.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.interceptor.matcher.InvocationMatcher;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A {@link SwitchInterceptor} maintains a list of {@link org.apache.commons.proxy2.interceptor.matcher.InvocationMatcher}/{@link Interceptor} pairs.  Each
+ * invocation will be checked against the registered InvocationMatchers.  If one matches the current invocation, then
+ * the corresponding Interceptor will be called.  If no InvocationMatchers match, then the invocation will merely
+ * {@link org.apache.commons.proxy2.Invocation#proceed()} method is called.
+ */
+public class SwitchInterceptor implements Interceptor, Serializable
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private static final long serialVersionUID = 1L;
+
+    private final List<Pair<InvocationMatcher, Interceptor>> cases = new CopyOnWriteArrayList<Pair<InvocationMatcher, Interceptor>>();
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public SwitchInterceptor()
+    {
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Interceptor Implementation
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Override
+    public Object intercept(Invocation invocation) throws Throwable
+    {
+        for (Pair<InvocationMatcher, Interceptor> currentCase : cases)
+        {
+            if (currentCase.getLeft().matches(invocation))
+            {
+                return currentCase.getRight().intercept(invocation);
+            }
+        }
+        return invocation.proceed();
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    public CaseBuilder when(InvocationMatcher matcher)
+    {
+        return new CaseBuilder(matcher);
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Inner Classes
+//----------------------------------------------------------------------------------------------------------------------
+
+    public class CaseBuilder
+    {
+        private final InvocationMatcher matcher;
+
+        public CaseBuilder(InvocationMatcher matcher)
+        {
+            this.matcher = matcher;
+        }
+
+        public SwitchInterceptor then(Interceptor interceptor)
+        {
+            cases.add(new ImmutablePair<InvocationMatcher, Interceptor>(matcher, interceptor));
+            return SwitchInterceptor.this;
+        }
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/ThrowingInterceptor.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/ThrowingInterceptor.java
new file mode 100644
index 0000000..9201e29
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/ThrowingInterceptor.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.ObjectProvider;
+
+public class ThrowingInterceptor implements Interceptor
+{
+	private static final long serialVersionUID = 1L;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+	private final ObjectProvider<? extends Exception> provider;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public ThrowingInterceptor(ObjectProvider<? extends Exception> provider)
+    {
+        this.provider = provider;
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Interceptor Implementation
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Override
+    public Object intercept(Invocation invocation) throws Throwable
+    {
+        throw provider.getObject();
+    }
+}
diff --git a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/ArgumentMatcher.java
similarity index 66%
copy from src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
copy to core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/ArgumentMatcher.java
index f73551d..db3c586 100644
--- a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/ArgumentMatcher.java
@@ -15,17 +15,13 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.util;
+package org.apache.commons.proxy2.interceptor.matcher;
 
-/**
- * @author James Carman
- * @since 1.0
- */
-public interface DuplicateEcho
+public interface ArgumentMatcher<T>
 {
-//**********************************************************************************************************************
+//----------------------------------------------------------------------------------------------------------------------
 // Other Methods
-//**********************************************************************************************************************
+//----------------------------------------------------------------------------------------------------------------------
 
-    public String echoBack( String message );
+    boolean matches(T argument);
 }
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/InvocationMatcher.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/InvocationMatcher.java
new file mode 100644
index 0000000..a439b01
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/InvocationMatcher.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher;
+
+import org.apache.commons.proxy2.Invocation;
+
+/**
+ * An {@link InvocationMatcher} is used to conditionally match {@link Invocation} objects based on
+ * some criteria such as method name, parameter values, etc.
+ */
+public interface InvocationMatcher
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    boolean matches(Invocation invocation);
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/argument/ArgumentMatcherUtils.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/argument/ArgumentMatcherUtils.java
new file mode 100644
index 0000000..bc7e295
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/argument/ArgumentMatcherUtils.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher.argument;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.proxy2.interceptor.matcher.ArgumentMatcher;
+
+public final class ArgumentMatcherUtils
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Static Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    public static <T> ArgumentMatcher<T> any()
+    {
+        return new AnyMatcher<T>();
+    }
+
+    public static ArgumentMatcher<String> endsWith(String suffix)
+    {
+        return new EndsWithMatcher(Validate.notNull(suffix));
+    }
+
+    public static <T> ArgumentMatcher<T> eq(final T value)
+    {
+        return new EqualsMatcher<T>(value);
+    }
+
+    public static <C extends Comparable<?>> ArgumentMatcher<C> gt(C comparable)
+    {
+        return new GreaterThanMatcher<C>(comparable);
+    }
+
+    public static <C extends Comparable<?>> ArgumentMatcher<C> gte(C comparable)
+    {
+        return new GreaterThanOrEqualMatcher<C>(comparable);
+    }
+
+    public static <T> ArgumentMatcher<T> isA(final Class<?> type)
+    {
+        return new InstanceOfMatcher<T>(type);
+    }
+
+    public static <T> ArgumentMatcher<T> isNull()
+    {
+        return new IsNullMatcher<T>();
+    }
+
+    public static <C extends Comparable<?>> ArgumentMatcher<C> lt(C comparable)
+    {
+        return new LessThanMatcher<C>(comparable);
+    }
+
+    public static <C extends Comparable<?>> ArgumentMatcher<C> lte(C comparable)
+    {
+        return new LessThanOrEqualMatcher<C>(comparable);
+    }
+
+    public static ArgumentMatcher<String> matches(String regex)
+    {
+        return new RegexMatcher(Validate.notNull(regex));
+    }
+
+    public static <T> ArgumentMatcher<T> notNull()
+    {
+        return new NotNullMatcher<T>();
+    }
+
+    public static ArgumentMatcher<String> startsWith(String prefix)
+    {
+        return new StartsWithMatcher(Validate.notNull(prefix));
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    private ArgumentMatcherUtils()
+    {
+
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Inner Classes
+//----------------------------------------------------------------------------------------------------------------------
+
+    private static final class AnyMatcher<T> implements ArgumentMatcher<T>
+    {
+        @Override
+        public boolean matches(T argument)
+        {
+            return true;
+        }
+    }
+
+    private abstract static class ComparatorMatcher<C extends Comparable<?>> implements ArgumentMatcher<C>
+    {
+        private final C comparable;
+
+        protected ComparatorMatcher(C comparable)
+        {
+            this.comparable = Validate.notNull(comparable);
+        }
+
+        protected abstract boolean evaluate(int comparison);
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public boolean matches(C argument)
+        {
+            if (argument == null)
+            {
+                return false;
+            }
+            @SuppressWarnings("rawtypes")
+            final int comparison = ((Comparable) comparable).compareTo(argument);
+            return evaluate(comparison);
+        }
+    }
+
+    public static class EndsWithMatcher implements ArgumentMatcher<String>
+    {
+        private final String suffix;
+
+        public EndsWithMatcher(String suffix)
+        {
+            this.suffix = suffix;
+        }
+
+        @Override
+        public boolean matches(String argument)
+        {
+        	return StringUtils.endsWith(argument, suffix);
+        }
+    }
+
+    private static final class EqualsMatcher<T> implements ArgumentMatcher<T>
+    {
+        private final T value;
+
+        public EqualsMatcher(T value)
+        {
+            this.value = value;
+        }
+
+        @Override
+        public boolean matches(T argument)
+        {
+            return ObjectUtils.equals(argument, value);
+        }
+    }
+
+    private static final class GreaterThanMatcher<C extends Comparable<?>> extends ComparatorMatcher<C>
+    {
+        private GreaterThanMatcher(C comparable)
+        {
+            super(comparable);
+        }
+
+        @Override
+        protected boolean evaluate(int comparison)
+        {
+            return comparison < 0;
+        }
+    }
+
+    private static final class GreaterThanOrEqualMatcher<C extends Comparable<?>> extends ComparatorMatcher<C>
+    {
+        private GreaterThanOrEqualMatcher(C comparable)
+        {
+            super(comparable);
+        }
+
+        @Override
+        protected boolean evaluate(int comparison)
+        {
+            return comparison <= 0;
+        }
+    }
+
+    private static final class InstanceOfMatcher<T> implements ArgumentMatcher<T>
+    {
+        private final Class<?> type;
+
+        public InstanceOfMatcher(Class<?> type)
+        {
+            this.type = Validate.notNull(type, "type");
+        }
+
+        @Override
+        public boolean matches(T argument)
+        {
+            return type.isInstance(argument);
+        }
+    }
+
+    private static final class IsNullMatcher<T> implements ArgumentMatcher<T>
+    {
+        @Override
+        public boolean matches(T argument)
+        {
+            return argument == null;
+        }
+    }
+
+    private static final class LessThanMatcher<C extends Comparable<?>> extends ComparatorMatcher<C>
+    {
+        private LessThanMatcher(C comparable)
+        {
+            super(comparable);
+        }
+
+        @Override
+        protected boolean evaluate(int comparison)
+        {
+            return comparison > 0;
+        }
+    }
+
+    private static final class LessThanOrEqualMatcher<C extends Comparable<?>> extends ComparatorMatcher<C>
+    {
+        private LessThanOrEqualMatcher(C comparable)
+        {
+            super(comparable);
+        }
+
+        @Override
+        protected boolean evaluate(int comparison)
+        {
+            return comparison >= 0;
+        }
+    }
+
+    private static final class NotNullMatcher<T> implements ArgumentMatcher<T>
+    {
+        @Override
+        public boolean matches(T argument)
+        {
+            return argument != null;
+        }
+    }
+
+    public static class RegexMatcher implements ArgumentMatcher<String>
+    {
+        private final String regex;
+
+        public RegexMatcher(String regex)
+        {
+            this.regex = regex;
+        }
+
+        @Override
+        public boolean matches(String argument)
+        {
+            return argument != null && argument.matches(regex);
+        }
+    }
+
+    private static final class StartsWithMatcher implements ArgumentMatcher<String>
+    {
+        private final String prefix;
+
+        private StartsWithMatcher(String prefix)
+        {
+            this.prefix = prefix;
+        }
+
+        @Override
+        public boolean matches(String argument)
+        {
+        	return StringUtils.startsWith(argument, prefix);
+        }
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/DeclaredByMatcher.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/DeclaredByMatcher.java
new file mode 100644
index 0000000..c881b0b
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/DeclaredByMatcher.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher.invocation;
+
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.interceptor.matcher.InvocationMatcher;
+
+/**
+ * InvocationMatcher based on declaring class of the method invoked.
+ */
+public class DeclaredByMatcher implements InvocationMatcher
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private final boolean exactMatch;
+    private final Class<?> declaredByType;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    /**
+     * Equivalent to {@link #DeclaredByMatcher(Class, boolean)}{@code (declaredByType, false)}
+     * @param declaredByType
+     */
+    public DeclaredByMatcher(Class<?> declaredByType)
+    {
+        this(declaredByType, false);
+    }
+
+    /**
+     * Create a {@link DeclaredByMatcher} instance.
+     * 
+     * @param declaredByType
+     *            type by which method must be declared
+     * @param exactMatch
+     *            if {@code false}, {@code declaredByType} may be a subclass of
+     *            the actual declaring class of the invocation method.
+     */
+    public DeclaredByMatcher(Class<?> declaredByType, boolean exactMatch)
+    {
+        this.declaredByType = declaredByType;
+        this.exactMatch = exactMatch;
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// InvocationMatcher Implementation
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Override
+    public boolean matches(Invocation invocation)
+    {
+        final Class<?> owner = invocation.getMethod().getDeclaringClass();
+        return exactMatch ?
+                declaredByType.equals(owner) :
+                owner.isAssignableFrom(declaredByType);
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/MethodNameMatcher.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/MethodNameMatcher.java
new file mode 100644
index 0000000..7eaa7ef
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/MethodNameMatcher.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher.invocation;
+
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.interceptor.matcher.InvocationMatcher;
+
+/**
+ * A {@link MethodNameMatcher} simply checks to see that the method name of the invocation matches the target method
+ * name given in the constructor.
+ */
+public class MethodNameMatcher implements InvocationMatcher
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private final String methodName;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public MethodNameMatcher(String methodName)
+    {
+        this.methodName = methodName;
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// InvocationMatcher Implementation
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Override
+    public boolean matches(Invocation invocation)
+    {
+        return methodName.equals(invocation.getMethod().getName());
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/ReturnTypeMatcher.java b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/ReturnTypeMatcher.java
new file mode 100644
index 0000000..9b7d743
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/interceptor/matcher/invocation/ReturnTypeMatcher.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher.invocation;
+
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.interceptor.matcher.InvocationMatcher;
+
+public class ReturnTypeMatcher implements InvocationMatcher
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private final boolean exactMatch;
+    private final Class<?> returnType;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public ReturnTypeMatcher(Class<?> returnType)
+    {
+        this(returnType, false);
+    }
+
+    public ReturnTypeMatcher(Class<?> returnType, boolean exactMatch)
+    {
+        this.returnType = returnType;
+        this.exactMatch = exactMatch;
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// InvocationMatcher Implementation
+//----------------------------------------------------------------------------------------------------------------------
+
+
+    @Override
+    public boolean matches(Invocation invocation)
+    {
+        return exactMatch ?
+                returnType.equals(invocation.getMethod().getReturnType()) :
+                returnType.isAssignableFrom(invocation.getMethod().getReturnType());
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/invoker/DelegatingInvoker.java b/core/src/main/java/org/apache/commons/proxy2/invoker/DelegatingInvoker.java
new file mode 100644
index 0000000..4607847
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/invoker/DelegatingInvoker.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.invoker;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ObjectProvider;
+
+/**
+ * Delegates a method invocation to the object provided by an
+ * {@link ObjectProvider}.
+ * 
+ * @param <T>
+ */
+public class DelegatingInvoker<T> implements Invoker
+{
+    private static final long serialVersionUID = 1L;
+
+    private final ObjectProvider<? extends T> delegateProvider;
+
+    public DelegatingInvoker(ObjectProvider<? extends T> delegateProvider)
+    {
+        super();
+        this.delegateProvider = Validate.notNull(delegateProvider);
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable
+    {
+        try
+        {
+            return method.invoke(delegateProvider.getObject(), arguments);
+        }
+        catch (InvocationTargetException e)
+        {
+            throw e.getTargetException();
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/proxy/invoker/DuckTypingInvoker.java b/core/src/main/java/org/apache/commons/proxy2/invoker/DuckTypingInvoker.java
similarity index 72%
rename from src/main/java/org/apache/commons/proxy/invoker/DuckTypingInvoker.java
rename to core/src/main/java/org/apache/commons/proxy2/invoker/DuckTypingInvoker.java
index 1992594..0e6a11d 100644
--- a/src/main/java/org/apache/commons/proxy/invoker/DuckTypingInvoker.java
+++ b/core/src/main/java/org/apache/commons/proxy2/invoker/DuckTypingInvoker.java
@@ -1,94 +1,104 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.Invoker;
-import org.apache.commons.proxy.ObjectProvider;
-
-import java.lang.reflect.Method;
-
-/**
- * An invoker which supports <a href="http://en.wikipedia.org/wiki/Duck_typing">&quot;duck typing&quot;</a>, meaning
- * that it finds a matching method on the object returned from the target provider and invokes it.  This class is
- * useful for adapting an existing class to an interface it does not implement.
- * <p>
- * <b>Example:</b>
- * </p>
- * <p>
- * <pre>
- * public class LegacyDuck // Does not implement interface!
- * {
- *   public void quack()
- *   {
- *     // Quacking logic...
- *   }
- * }
- * <p/>
- * public interface Duck
- * {
- *   public void quack();
- * }
- * <p/>
- * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck
- * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
- * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } );
- * </pre>
- * </p>
- */
-public class DuckTypingInvoker implements Invoker
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final ObjectProvider targetProvider;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public DuckTypingInvoker( final ObjectProvider targetProvider )
-    {
-        this.targetProvider = targetProvider;
-    }
-
-//**********************************************************************************************************************
-// Invoker Implementation
-//**********************************************************************************************************************
-
-    public Object invoke( final Object proxy, final Method method, final Object[] arguments ) throws Throwable
-    {
-        final Object target = targetProvider.getObject();
-        final Class targetClass = target.getClass();
-        try
-        {
-            final Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
-            if( method.getReturnType().isAssignableFrom(targetMethod.getReturnType()) )
-            {
-                return targetMethod.invoke(target, arguments);
-            }
-            throw new UnsupportedOperationException(
-                    "Target type " + targetClass.getName() + " method has incompatible return type.");
-        }
-        catch( NoSuchMethodException e )
-        {
-            throw new UnsupportedOperationException(
-                    "Target type " + targetClass.getName() + " does not have a method matching " + method + ".");
-        }
-    }
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.invoker;

+

+import org.apache.commons.proxy2.Invoker;

+import org.apache.commons.proxy2.ObjectProvider;

+

+import java.lang.reflect.Method;

+

+/**

+ * An invoker which supports <a href="http://en.wikipedia.org/wiki/Duck_typing">&quot;duck typing&quot;</a>, meaning

+ * that it finds a matching method on the object returned from the target provider and invokes it.  This class is

+ * useful for adapting an existing class to an interface it does not implement.

+ * <p>

+ * <b>Example:</b>

+ * </p>

+ * <p>

+ * <pre>

+ * public class LegacyDuck // Does not implement interface!

+ * {

+ *   public void quack()

+ *   {

+ *     // Quacking logic...

+ *   }

+ * }

+ * <p/>

+ * public interface Duck

+ * {

+ *   public void quack();

+ * }

+ * <p/>

+ * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck

+ * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);

+ * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } );

+ * </pre>

+ * </p>

+ */

+public class DuckTypingInvoker implements Invoker

+{

+    /** Serialization version */

+    private static final long serialVersionUID = 1L;

+

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private final ObjectProvider<?> targetProvider;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new DuckTypingInvoker instance.

+     * @param targetProvider

+     */

+    public DuckTypingInvoker( final ObjectProvider<?> targetProvider )

+    {

+        this.targetProvider = targetProvider;

+    }

+

+  //**********************************************************************************************************************

+ // Invoker Implementation

+ //**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    public Object invoke( final Object proxy, final Method method, final Object[] arguments ) throws Throwable

+    {

+        final Object target = targetProvider.getObject();

+        final Class<?> targetClass = target.getClass();

+        try

+        {

+            final Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());

+            if( method.getReturnType().isAssignableFrom(targetMethod.getReturnType()) )

+            {

+                return targetMethod.invoke(target, arguments);

+            }

+            throw new UnsupportedOperationException(

+                    "Target type " + targetClass.getName() + " method has incompatible return type.");

+        }

+        catch( NoSuchMethodException e )

+        {

+            throw new UnsupportedOperationException(

+                    "Target type " + targetClass.getName() + " does not have a method matching " + method + ".", e);

+        }

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/invoker/InvocationHandlerAdapter.java b/core/src/main/java/org/apache/commons/proxy2/invoker/InvocationHandlerAdapter.java
similarity index 67%
rename from src/main/java/org/apache/commons/proxy/invoker/InvocationHandlerAdapter.java
rename to core/src/main/java/org/apache/commons/proxy2/invoker/InvocationHandlerAdapter.java
index f59efb3..71714ba 100644
--- a/src/main/java/org/apache/commons/proxy/invoker/InvocationHandlerAdapter.java
+++ b/core/src/main/java/org/apache/commons/proxy2/invoker/InvocationHandlerAdapter.java
@@ -1,58 +1,67 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.Invoker;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-
-/**
- * An adapter class to adapt the JDK's {@link InvocationHandler} interface to Commons Proxy's
- * {@link Invoker} interface.
- *
- * @author James Carman
- * @since 1.0
- */
-public class InvocationHandlerAdapter implements Invoker
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final InvocationHandler invocationHandler;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public InvocationHandlerAdapter( InvocationHandler invocationHandler )
-    {
-        this.invocationHandler = invocationHandler;
-    }
-
-//**********************************************************************************************************************
-// Invoker Implementation
-//**********************************************************************************************************************
-
-
-    public Object invoke( Object proxy, Method method, Object[] arguments ) throws Throwable
-    {
-        return invocationHandler.invoke(proxy, method, arguments);
-    }
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.invoker;

+

+import org.apache.commons.proxy2.Invoker;

+

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.Method;

+

+/**

+ * An adapter class to adapt the JDK's {@link InvocationHandler} interface to Commons Proxy's

+ * {@link Invoker} interface.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class InvocationHandlerAdapter implements Invoker

+{

+    /** Serialization version */

+    private static final long serialVersionUID = 1L;

+

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private final InvocationHandler invocationHandler;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new InvocationHandlerAdapter instance.

+     * @param invocationHandler

+     */

+    public InvocationHandlerAdapter( InvocationHandler invocationHandler )

+    {

+        this.invocationHandler = invocationHandler;

+    }

+

+  //**********************************************************************************************************************

+ // Invoker Implementation

+ //**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    public Object invoke( Object proxy, Method method, Object[] arguments ) throws Throwable

+    {

+        return invocationHandler.invoke(proxy, method, arguments);

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/invoker/NullInvoker.java b/core/src/main/java/org/apache/commons/proxy2/invoker/NullInvoker.java
similarity index 62%
rename from src/main/java/org/apache/commons/proxy/invoker/NullInvoker.java
rename to core/src/main/java/org/apache/commons/proxy2/invoker/NullInvoker.java
index 093cb1e..1bcc33e 100644
--- a/src/main/java/org/apache/commons/proxy/invoker/NullInvoker.java
+++ b/core/src/main/java/org/apache/commons/proxy2/invoker/NullInvoker.java
@@ -1,46 +1,55 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.Invoker;
-import org.apache.commons.proxy.ProxyUtils;
-
-import java.io.Serializable;
-import java.lang.reflect.Method;
-
-/**
- * An {@link Invoker} implementation which merely returns null for all method invocations.  This class is
- * useful for scenarios where the "null object" design pattern is needed.
- *
- * @author James Carman
- * @since 1.0
- */
-public class NullInvoker implements Invoker, Serializable
-{
-//**********************************************************************************************************************
-// Invoker Implementation
-//**********************************************************************************************************************
-
-    public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
-    {
-        final Class returnType = method.getReturnType();
-        return ProxyUtils.getDefaultValue(returnType);
-    }
-
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.invoker;

+

+import org.apache.commons.proxy2.Invoker;

+import org.apache.commons.proxy2.ProxyUtils;

+

+import java.io.Serializable;

+import java.lang.reflect.Method;

+

+/**

+ * An {@link Invoker} implementation which merely returns null for all method invocations.  This class is

+ * useful for scenarios where the "null object" design pattern is needed.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class NullInvoker implements Invoker, Serializable

+{

+    /** Serialization version */

+    private static final long serialVersionUID = 1L;

+

+    /**

+     * Statically available instance.

+     */

+    public static final NullInvoker INSTANCE = new NullInvoker();

+

+  //**********************************************************************************************************************

+ // Invoker Implementation

+ //**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable

+    {

+        final Class<?> returnType = method.getReturnType();

+        return ProxyUtils.nullValue(returnType);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/invoker/RecordedInvocation.java b/core/src/main/java/org/apache/commons/proxy2/invoker/RecordedInvocation.java
new file mode 100644
index 0000000..ada8299
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/invoker/RecordedInvocation.java
@@ -0,0 +1,152 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.invoker;

+

+import org.apache.commons.lang3.ArrayUtils;

+import org.apache.commons.proxy2.ProxyUtils;

+

+import java.lang.reflect.Method;

+

+/**

+ * Detached representation of a method invocation.

+ *

+ * @author James Carman

+ */

+public class RecordedInvocation

+{

+//----------------------------------------------------------------------------------------------------------------------

+// Fields

+//----------------------------------------------------------------------------------------------------------------------

+

+    private final Method invokedMethod;

+    private final Object[] arguments;

+

+//----------------------------------------------------------------------------------------------------------------------

+// Constructors

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * Create a new RecordedInvocation instance.

+     *

+     * @param invokedMethod

+     * @param arguments

+     */

+    public RecordedInvocation(Method invokedMethod, Object[] arguments)

+    {

+        this.invokedMethod = invokedMethod;

+        this.arguments = ArrayUtils.nullToEmpty(ArrayUtils.clone(arguments));

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Getter/Setter Methods

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * Get the invokedMethod.

+     *

+     * @return Method

+     */

+    public Method getInvokedMethod()

+    {

+        return invokedMethod;

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Canonical Methods

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * {@inheritDoc}

+     */

+    public String toString()

+    {

+        StringBuilder buffer = new StringBuilder();

+        buffer.append(invokedMethod.getDeclaringClass().getName());

+        buffer.append(".");

+        buffer.append(invokedMethod.getName());

+        buffer.append("(");

+        int count = arguments.length;

+        for (int i = 0; i < count; i++)

+        {

+            Object arg = arguments[i];

+            if (i > 0)

+            {

+                buffer.append(", ");

+            }

+            convert(buffer, arg);

+        }

+        buffer.append(")");

+        return buffer.toString();

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Other Methods

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * Add a string representation of <code>input</code> to <code>buffer</code>.

+     *

+     * @param buffer

+     * @param input

+     */

+    protected void convert(StringBuilder buffer, Object input)

+    {

+        if (input == null)

+        {

+            buffer.append("<null>");

+            return;

+        }

+

+        // Primitive types, and non-object arrays

+        // use toString().

+        if (!(input instanceof Object[]))

+        {

+            buffer.append(input.toString());

+            return;

+        }

+        else

+        {

+            buffer.append("(");

+            buffer.append(ProxyUtils.getJavaClassName(input.getClass()));

+            buffer.append("){");

+            Object[] array = (Object[]) input;

+            int count = array.length;

+            for (int i = 0; i < count; i++)

+            {

+                if (i > 0)

+                {

+                    buffer.append(", ");

+                }

+                // We use convert() again, because it could be a multi-dimensional array

+                // where each element must be converted.

+                convert(buffer, array[i]);

+            }

+            buffer.append("}");

+        }

+    }

+

+    /**

+     * Get the arguments.

+     *

+     * @return Object[]

+     */

+    public Object[] getArguments()

+    {

+        return ArrayUtils.clone(arguments);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/invoker/recorder/InvocationRecorder.java b/core/src/main/java/org/apache/commons/proxy2/invoker/recorder/InvocationRecorder.java
new file mode 100644
index 0000000..20149d2
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/invoker/recorder/InvocationRecorder.java
@@ -0,0 +1,119 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.invoker.recorder;

+

+import org.apache.commons.lang3.reflect.TypeUtils;

+import org.apache.commons.proxy2.Invoker;

+import org.apache.commons.proxy2.ProxyFactory;

+import org.apache.commons.proxy2.ProxyUtils;

+import org.apache.commons.proxy2.invoker.RecordedInvocation;

+

+import java.lang.reflect.Method;

+import java.lang.reflect.Type;

+import java.util.LinkedList;

+import java.util.List;

+

+/**

+ * An {@link InvocationRecorder} records method invocations against its generated proxies.

+ * @author James Carman

+ */

+public class InvocationRecorder

+{

+    private final ProxyFactory proxyFactory;

+    private List<RecordedInvocation> recordedInvocations = new LinkedList<RecordedInvocation>();

+

+    /**

+     * Create a new InvocationRecorder instance.

+     * @param proxyFactory

+     */

+    public InvocationRecorder( ProxyFactory proxyFactory )

+    {

+        this.proxyFactory = proxyFactory;

+    }

+

+    /**

+     * Get the invocations that have been recorded up to this point.  The list is "live" and should not be modified.

+     * @return {@link List} of {@link RecordedInvocation}

+     */

+    public List<RecordedInvocation> getRecordedInvocations()

+    {

+        return recordedInvocations;

+    }

+

+    /**

+     * Generate a recording proxy for the specified class.

+     * @param <T>

+     * @param type

+     * @return the generated proxy

+     */

+    public <T> T proxy( Class<T> type )

+    {

+        return proxy(type, type);

+    }

+

+    /**

+     * Generate a recording proxy for the specified class, qualified as <code>genericType</code>.

+     * @param <T>

+     * @param genericType

+     * @param type

+     * @return the generated proxy

+     */

+    public <T> T proxy( Type genericType, Class<T> type )

+    {

+        if( proxyFactory.canProxy(type) )

+        {

+            @SuppressWarnings("unchecked")

+            final T result = (T) proxyFactory.createInvokerProxy(new InvocationRecorderInvoker(genericType), type);

+            return result;

+        }

+        return ProxyUtils.nullValue(type);

+    }

+

+    private final class InvocationRecorderInvoker implements Invoker

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final Type targetType;

+

+        private InvocationRecorderInvoker( Type targetType )

+        {

+            this.targetType = targetType;

+        }

+

+        /**

+         * {@inheritDoc}

+         */

+        public Object invoke( Object o, Method method, Object[] args ) throws Throwable

+        {

+            recordedInvocations.add(new RecordedInvocation(method, args));

+            final Class<?> returnType = TypeUtils.getRawType(method.getGenericReturnType(), targetType);

+            //what to do if returnType is null?

+            return proxy(method.getGenericReturnType(), returnType);

+        }

+    }

+

+    /**

+     * Reset this {@link InvocationRecorder}.

+     */

+    public void reset()

+    {

+        recordedInvocations.clear();

+    }

+

+}

diff --git a/src/main/java/org/apache/commons/proxy/provider/BeanProvider.java b/core/src/main/java/org/apache/commons/proxy2/provider/BeanProvider.java
similarity index 73%
rename from src/main/java/org/apache/commons/proxy/provider/BeanProvider.java
rename to core/src/main/java/org/apache/commons/proxy2/provider/BeanProvider.java
index f63003a..68b0d4c 100644
--- a/src/main/java/org/apache/commons/proxy/provider/BeanProvider.java
+++ b/core/src/main/java/org/apache/commons/proxy2/provider/BeanProvider.java
@@ -1,91 +1,81 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider;
-
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import java.io.Serializable;
-
-/**
- * Uses <code>Class.newInstance()</code> to instantiate an object.
- *
- * @author James Carman
- * @since 1.0
- */
-public class BeanProvider implements ObjectProvider, Serializable
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private Class beanClass;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public BeanProvider()
-    {
-    }
-
-    /**
-     * Constructs a provider which instantiates objects of the specified bean class.
-     *
-     * @param beanClass the bean class
-     */
-    public BeanProvider( Class beanClass )
-    {
-        this.beanClass = beanClass;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        try
-        {
-            if( beanClass == null )
-            {
-                throw new ObjectProviderException("No bean class provided.");
-            }
-            return beanClass.newInstance();
-        }
-        catch( InstantiationException e )
-        {
-            throw new ObjectProviderException("Class " + beanClass.getName() + " is not concrete.", e);
-        }
-        catch( IllegalAccessException e )
-        {
-            throw new ObjectProviderException("Constructor for class " + beanClass.getName() + " is not accessible.",
-                    e);
-        }
-    }
-
-//**********************************************************************************************************************
-// Getter/Setter Methods
-//**********************************************************************************************************************
-
-    public void setBeanClass( Class beanClass )
-    {
-        this.beanClass = beanClass;
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;

+

+import org.apache.commons.lang3.Validate;

+import org.apache.commons.proxy2.ObjectProvider;

+import org.apache.commons.proxy2.exception.ObjectProviderException;

+

+import java.io.Serializable;

+

+/**

+ * Uses <code>Class.newInstance()</code> to instantiate an object.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class BeanProvider<T> implements ObjectProvider<T>, Serializable

+{

+    /** Serialization version */

+    private static final long serialVersionUID = 1L;

+

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private final Class<? extends T> beanClass;

+

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+

+    /**

+     * Constructs a provider which instantiates objects of the specified bean class.

+     *

+     * @param beanClass the bean class

+     */

+    public BeanProvider( Class<? extends T> beanClass )

+    {

+        Validate.notNull(beanClass, "Bean class cannot be null.");

+        this.beanClass = beanClass;

+    }

+

+//**********************************************************************************************************************

+// ObjectProvider Implementation

+//**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    public T getObject()

+    {

+        try

+        {

+            return beanClass.newInstance();

+        }

+        catch( InstantiationException e )

+        {

+            throw new ObjectProviderException("Class " + beanClass.getName() + " is not concrete.", e);

+        }

+        catch( IllegalAccessException e )

+        {

+            throw new ObjectProviderException("Constructor for class " + beanClass.getName() + " is not accessible.",

+                    e);

+        }

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/provider/CloningProvider.java b/core/src/main/java/org/apache/commons/proxy2/provider/CloningProvider.java
similarity index 60%
rename from src/main/java/org/apache/commons/proxy/provider/CloningProvider.java
rename to core/src/main/java/org/apache/commons/proxy2/provider/CloningProvider.java
index b99fcee..2b494c4 100644
--- a/src/main/java/org/apache/commons/proxy/provider/CloningProvider.java
+++ b/core/src/main/java/org/apache/commons/proxy2/provider/CloningProvider.java
@@ -1,101 +1,103 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider;
-
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.ProxyUtils;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import java.io.Serializable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Merely calls <code>clone()</code> (reflectively) on the given {@link Cloneable} object.
- *
- * @author James Carman
- * @since 1.0
- */
-public class CloningProvider implements ObjectProvider, Serializable
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Cloneable cloneable;
-    private Method cloneMethod;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    /**
-     * Constructs a provider which returns clone copies of the specified {@link Cloneable}
-     * object.
-     *
-     * @param cloneable the object to clone
-     */
-    public CloningProvider( Cloneable cloneable )
-    {
-        this.cloneable = cloneable;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-
-    public Object getObject()
-    {
-        try
-        {
-            return getCloneMethod().invoke(cloneable, ProxyUtils.EMPTY_ARGUMENTS);
-        }
-        catch( IllegalAccessException e )
-        {
-            throw new ObjectProviderException(
-                    "Class " + cloneable.getClass().getName() + " does not have a public clone() method.", e);
-        }
-        catch( InvocationTargetException e )
-        {
-            throw new ObjectProviderException(
-                    "Attempt to clone object of type " + cloneable.getClass().getName() + " threw an exception.", e);
-        }
-    }
-
-//**********************************************************************************************************************
-// Getter/Setter Methods
-//**********************************************************************************************************************
-
-    private synchronized Method getCloneMethod()
-    {
-        if( cloneMethod == null )
-        {
-            try
-            {
-                cloneMethod = cloneable.getClass().getMethod("clone", ProxyUtils.EMPTY_ARGUMENT_TYPES);
-            }
-            catch( NoSuchMethodException e )
-            {
-                throw new ObjectProviderException(
-                        "Class " + cloneable.getClass().getName() + " does not have a public clone() method.");
-            }
-        }
-        return cloneMethod;
-    }
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;

+

+import org.apache.commons.lang3.Validate;

+import org.apache.commons.lang3.reflect.MethodUtils;

+import org.apache.commons.proxy2.ObjectProvider;

+import org.apache.commons.proxy2.exception.ObjectProviderException;

+

+import java.io.Serializable;

+import java.lang.reflect.InvocationTargetException;

+

+/**

+ * Merely calls <code>clone()</code> (reflectively) on the given {@link Cloneable} object.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class CloningProvider<T extends Cloneable> implements ObjectProvider<T>, Serializable

+{

+    /**

+     * Serialization version

+     */

+    private static final long serialVersionUID = 1L;

+

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private final T cloneable;

+

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+

+    /**

+     * Constructs a provider which returns clone copies of the specified {@link Cloneable}

+     * object.

+     *

+     * @param cloneable the object to clone

+     */

+    public CloningProvider(T cloneable)

+    {

+        Validate.notNull(cloneable, "Cloneable object cannot be null.");

+        Validate.isTrue(

+                MethodUtils.getAccessibleMethod(cloneable.getClass(), "clone") != null,

+                String.format("Class %s does not override clone() method as public.",

+                        cloneable.getClass().getName()));

+        this.cloneable = cloneable;

+    }

+

+    //**********************************************************************************************************************

+    // ObjectProvider Implementation

+    //**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    @SuppressWarnings("unchecked")

+    public T getObject()

+    {

+        try

+        {

+            return (T) MethodUtils.invokeExactMethod(cloneable, "clone");

+        }

+        catch (IllegalAccessException e)

+        {

+            throw new ObjectProviderException(

+                    "Class " + cloneable.getClass().getName() + " does not have a public clone() method.", e);

+        }

+        catch (InvocationTargetException e)

+        {

+            throw new ObjectProviderException(

+                    "Attempt to clone object of type " + cloneable.getClass().getName() + " threw an exception.", e);

+        }

+        catch (NoSuchMethodException e)

+        {

+            throw new ObjectProviderException(

+                    String.format("Class %s does not have a clone() method (should never happen).", cloneable.getClass().getName()), e);

+        }

+    }

+

+//**********************************************************************************************************************

+// Getter/Setter Methods

+//**********************************************************************************************************************

+

+

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/provider/ConstantProvider.java b/core/src/main/java/org/apache/commons/proxy2/provider/ConstantProvider.java
new file mode 100644
index 0000000..55ca010
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/provider/ConstantProvider.java
@@ -0,0 +1,65 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;

+

+import org.apache.commons.proxy2.ObjectProvider;

+

+import java.io.Serializable;

+

+/**

+ * Always returns the same object.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class ConstantProvider<T> implements ObjectProvider<T>, Serializable

+{

+    /** Serialization version */

+    private static final long serialVersionUID = 1L;

+

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private final T constant;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new ConstantProvider instance.

+     * @param constant

+     */

+    public ConstantProvider( T constant )

+    {

+        this.constant = constant;

+    }

+

+  //**********************************************************************************************************************

+ // ObjectProvider Implementation

+ //**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    public T getObject()

+    {

+        return constant;

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/provider/NullProvider.java b/core/src/main/java/org/apache/commons/proxy2/provider/NullProvider.java
similarity index 64%
rename from src/main/java/org/apache/commons/proxy/provider/NullProvider.java
rename to core/src/main/java/org/apache/commons/proxy2/provider/NullProvider.java
index 7abee17..8aca99b 100644
--- a/src/main/java/org/apache/commons/proxy/provider/NullProvider.java
+++ b/core/src/main/java/org/apache/commons/proxy2/provider/NullProvider.java
@@ -1,37 +1,42 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider;
-
-/**
- * Always returns null.
- *
- * @author James Carman
- * @since 1.0
- */
-public class NullProvider extends ConstantProvider
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public NullProvider()
-    {
-        super(null);
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;

+

+/**

+ * Always returns null.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class NullProvider<T> extends ConstantProvider<T>

+{

+    /** Serialization version */

+    private static final long serialVersionUID = 1L;

+

+  //**********************************************************************************************************************

+ // Constructors

+ //**********************************************************************************************************************

+

+    /**

+     * Create a new NullProvider instance.

+     */

+    public NullProvider()

+    {

+        super(null);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/provider/ObjectProviderUtils.java b/core/src/main/java/org/apache/commons/proxy2/provider/ObjectProviderUtils.java
new file mode 100644
index 0000000..8b8204e
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/provider/ObjectProviderUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;
+
+import org.apache.commons.proxy2.ObjectProvider;
+
+public final class ObjectProviderUtils
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Static Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    public static <T> ObjectProvider<T> bean(Class<T> beanClass)
+    {
+        return new BeanProvider<T>(beanClass);
+    }
+
+    public static <T extends Cloneable> ObjectProvider<T> cloning(T prototype)
+    {
+        return new CloningProvider<T>(prototype);
+    }
+
+    public static <T> ObjectProvider<T> constant(T value)
+    {
+        return new ConstantProvider<T>(value);
+    }
+
+    public static <T> ObjectProvider<T> nullValue()
+    {
+        return new NullProvider<T>();
+    }
+
+    public static <T> ObjectProvider<T> singleton(ObjectProvider<T> inner)
+    {
+        return new SingletonProvider<T>(inner);
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    private ObjectProviderUtils()
+    {
+        
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/provider/ProviderDecorator.java b/core/src/main/java/org/apache/commons/proxy2/provider/ProviderDecorator.java
new file mode 100644
index 0000000..ded2907
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/provider/ProviderDecorator.java
@@ -0,0 +1,80 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;

+

+import org.apache.commons.proxy2.ObjectProvider;

+

+/**

+ * Returns the result of the inner {@link ObjectProvider provider}.  Subclasses can override the {@link #getObject()}

+ * method and decorate what comes back from the inner provider in some way (by {@link SingletonProvider caching it} for

+ * example).

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class ProviderDecorator<T> implements ObjectProvider<T>

+{

+//----------------------------------------------------------------------------------------------------------------------

+// Fields

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * The wrapped {@link ObjectProvider}.

+     */

+    private ObjectProvider<? extends T> inner;

+

+//----------------------------------------------------------------------------------------------------------------------

+// Constructors

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * Create a new ProviderDecorator instance.

+     *

+     * @param inner

+     */

+    public ProviderDecorator(ObjectProvider<? extends T> inner)

+    {

+        this.inner = inner;

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// ObjectProvider Implementation

+//----------------------------------------------------------------------------------------------------------------------

+

+    /**

+     * {@inheritDoc}

+     */

+    public T getObject()

+    {

+        return inner.getObject();

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Getter/Setter Methods

+//----------------------------------------------------------------------------------------------------------------------

+

+    protected ObjectProvider<? extends T> getInner()

+    {

+        return inner;

+    }

+

+    public void setInner(ObjectProvider<? extends T> inner)

+    {

+        this.inner = inner;

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/provider/SingletonProvider.java b/core/src/main/java/org/apache/commons/proxy2/provider/SingletonProvider.java
similarity index 77%
rename from src/main/java/org/apache/commons/proxy/provider/SingletonProvider.java
rename to core/src/main/java/org/apache/commons/proxy2/provider/SingletonProvider.java
index 45f0581..ceda06a 100644
--- a/src/main/java/org/apache/commons/proxy/provider/SingletonProvider.java
+++ b/core/src/main/java/org/apache/commons/proxy2/provider/SingletonProvider.java
@@ -1,64 +1,71 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider;
-
-import org.apache.commons.proxy.ObjectProvider;
-
-import java.io.Serializable;
-
-/**
- * Wraps another object provider, making sure to only call it once, returning the value returned from the wrapped
- * provider on all subsequent invocations.
- *
- * @author James Carman
- * @since 1.0
- */
-public class SingletonProvider extends ProviderDecorator implements Serializable
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private transient Object instance;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public SingletonProvider( ObjectProvider inner )
-    {
-        super(inner);
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        synchronized( this )
-        {
-            if( instance == null )
-            {
-                instance = super.getObject();
-            }
-        }
-        return instance;
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;

+

+import org.apache.commons.proxy2.ObjectProvider;

+

+/**

+ * Wraps another object provider, making sure to only call it once, returning the value returned from the wrapped

+ * provider on all subsequent invocations.

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public class SingletonProvider<T> extends ProviderDecorator<T>

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private T instance;

+

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+

+    /**

+     * Create a new SingletonProvider instance.

+     *

+     * @param inner

+     */

+    public SingletonProvider(ObjectProvider<? extends T> inner)

+    {

+        super(inner);

+    }

+

+//**********************************************************************************************************************

+// ObjectProvider Implementation

+//**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    public T getObject()

+    {

+        synchronized (this)

+        {

+            if (instance == null)

+            {

+                instance = super.getObject();

+                // Garbage collection

+                setInner(null);

+            }

+            return instance;

+        }

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java
new file mode 100644
index 0000000..48dafd8
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Map;
+
+import org.apache.commons.lang3.AnnotationUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.lang3.reflect.TypeUtils;
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ObjectProvider;
+import org.apache.commons.proxy2.ProxyFactory;
+import org.apache.commons.proxy2.ProxyUtils;
+import org.apache.commons.proxy2.impl.AbstractProxyFactory;
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+
+public class AnnotationBuilder<A extends Annotation> extends StubBuilder<A>
+{
+    // underlying proxyfactory implementation based on
+    // org.apache.commons.proxy2.jdk.JdkProxyFactory
+
+    private static class InterceptorInvocationHandler implements InvocationHandler, Serializable
+    {
+        /** Serialization version */
+        private static final long serialVersionUID = 1L;
+
+        private final ObjectProvider<?> provider;
+        private final Interceptor methodInterceptor;
+
+        public InterceptorInvocationHandler(ObjectProvider<?> provider, Interceptor methodInterceptor)
+        {
+            this.provider = provider;
+            this.methodInterceptor = methodInterceptor;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+        {
+            if (ProxyUtils.isHashCode(method))
+            {
+                return AnnotationUtils.hashCode((Annotation) proxy);
+            }
+            if (ProxyUtils.isEqualsMethod(method))
+            {
+                return args[0] instanceof Annotation
+                        && AnnotationUtils.equals((Annotation) proxy, (Annotation) args[0]);
+            }
+            if ("toString".equals(method.getName()) && method.getParameterTypes().length == 0)
+            {
+                return AnnotationUtils.toString((Annotation) proxy);
+            }
+            final ReflectionInvocation invocation = new ReflectionInvocation(provider.getObject(), method, args);
+            return methodInterceptor.intercept(invocation);
+        }
+
+    }
+
+    private static class ReflectionInvocation implements Invocation, Serializable
+    {
+        /** Serialization version */
+        private static final long serialVersionUID = 1L;
+
+        private final Method method;
+        private final Object[] arguments;
+        private final Object target;
+
+        public ReflectionInvocation(Object target, Method method, Object[] arguments)
+        {
+            this.method = method;
+            this.arguments = (arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments);
+            this.target = target;
+        }
+
+        public Object[] getArguments()
+        {
+            return arguments;
+        }
+
+        public Method getMethod()
+        {
+            return method;
+        }
+
+        public Object getProxy()
+        {
+            return target;
+        }
+
+        public Object proceed() throws Throwable
+        {
+            try
+            {
+                return method.invoke(target, arguments);
+            }
+            catch (InvocationTargetException e)
+            {
+                throw e.getTargetException();
+            }
+        }
+    }
+
+    private static final ProxyFactory PROXY_FACTORY = new AbstractProxyFactory()
+    {
+        @SuppressWarnings("unchecked")
+        public <T> T createInvokerProxy(ClassLoader classLoader, final Invoker invoker, Class<?>... proxyClasses)
+        {
+            return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InvocationHandler()
+            {
+                @Override
+                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+                {
+                    return invoker.invoke(proxy, method, args);
+                }
+            });
+        }
+
+        @SuppressWarnings("unchecked")
+        public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
+                Class<?>... proxyClasses)
+        {
+            return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(
+                    ObjectProviderUtils.constant(target), interceptor));
+        }
+
+        @SuppressWarnings("unchecked")
+        public <T> T createDelegatorProxy(ClassLoader classLoader, final ObjectProvider<?> delegateProvider,
+                Class<?>... proxyClasses)
+        {
+            return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(
+                    delegateProvider, new Interceptor()
+                    {
+                        private static final long serialVersionUID = 1L;
+
+                        @Override
+                        public Object intercept(Invocation invocation) throws Throwable
+                        {
+                            return invocation.proceed();
+                        }
+                    }));
+        }
+    };
+
+    private class MapAnnotationTrainer extends AnnotationTrainer<A>
+    {
+        final Map<String, ?> members;
+
+        MapAnnotationTrainer(Map<String, ?> members)
+        {
+            super(annotationType);
+            this.members = members;
+        }
+
+        @Override
+        protected void train(A trainee)
+        {
+            WhenObject<Object> bud;
+            AnnotationTrainer<A> dy = this;
+            for (Map.Entry<String, ?> attr : members.entrySet())
+            {
+                final Method m;
+                try
+                {
+                    m = traineeType.getDeclaredMethod(attr.getKey());
+                }
+                catch (Exception e1)
+                {
+                    throw new IllegalArgumentException(String.format("Could not detect annotation member %1$s",
+                            attr.getKey()));
+                }
+                try
+                {
+                    bud = dy.when(m.invoke(trainee));
+                }
+                catch (Exception e)
+                {
+                    // it must have happened on the invoke, so we didn't call
+                    // when... it shouldn't happen, but we'll simply skip:
+                    continue;
+                }
+                final Object value = attr.getValue();
+                Validate.isTrue(TypeUtils.isInstance(value, m.getReturnType()), "Value %s can not be assigned to %s",
+                        value, m.getReturnType());
+                dy = bud.thenReturn(value);
+            }
+        }
+    }
+
+    public static <A extends Annotation> A buildDefault(Class<A> type)
+    {
+        return of(type).build();
+    }
+
+    public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type)
+    {
+        return new AnnotationBuilder<A>(type, AnnotationInvoker.INSTANCE);
+    }
+
+    public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type, ObjectProvider<? extends A> provider)
+    {
+        return new AnnotationBuilder<A>(type, provider);
+    }
+
+    public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type, A target)
+    {
+        return new AnnotationBuilder<A>(type, target);
+    }
+
+    private final Class<A> annotationType;
+
+    private AnnotationBuilder(Class<A> type, Invoker invoker)
+    {
+        super(PROXY_FACTORY, type, invoker);
+        this.annotationType = type;
+        train(new AnnotationTypeTrainer<A>(type));
+    }
+
+    private AnnotationBuilder(Class<A> type, ObjectProvider<? extends A> provider)
+    {
+        super(PROXY_FACTORY, type, provider);
+        this.annotationType = type;
+        train(new AnnotationTypeTrainer<A>(type));
+    }
+
+    private AnnotationBuilder(Class<A> type, A target)
+    {
+        super(PROXY_FACTORY, type, target);
+        this.annotationType = type;
+        train(new AnnotationTypeTrainer<A>(type));
+    }
+
+    public AnnotationBuilder<A> withMembers(Map<String, ?> members)
+    {
+        return train(new MapAnnotationTrainer(members));
+    }
+
+    @Override
+    public <O> AnnotationBuilder<A> train(BaseTrainer<?, O> trainer)
+    {
+        return (AnnotationBuilder<A>) super.train(trainer);
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java
new file mode 100644
index 0000000..6e0b0e6
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import java.lang.reflect.Method;
+
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ProxyUtils;
+
+public final class AnnotationInvoker implements Invoker
+{
+    private static final long serialVersionUID = 1L;
+
+    public static final AnnotationInvoker INSTANCE = new AnnotationInvoker();
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
+        final Object result = method.getDefaultValue();
+        return result == null && method.getReturnType().isPrimitive() ? ProxyUtils
+            .nullValue(method.getReturnType()) : result;
+    }
+    
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java
similarity index 65%
copy from src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
copy to core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java
index f73551d..a808deb 100644
--- a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java
@@ -15,17 +15,18 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.util;
+package org.apache.commons.proxy2.stub;
 
-/**
- * @author James Carman
- * @since 1.0
- */
-public interface DuplicateEcho
+import java.lang.annotation.Annotation;
+
+public abstract class AnnotationTrainer<A extends Annotation> extends BaseAnnotationTrainer<AnnotationTrainer<A>, A>
 {
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
+    protected AnnotationTrainer() {
+        super();
+    }
 
-    public String echoBack( String message );
+    protected AnnotationTrainer(Class<A> traineeType) {
+        super(traineeType);
+    }
+
 }
diff --git a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTypeTrainer.java
similarity index 65%
copy from src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
copy to core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTypeTrainer.java
index f73551d..5754458 100644
--- a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTypeTrainer.java
@@ -15,17 +15,22 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.util;
+package org.apache.commons.proxy2.stub;
 
-/**
- * @author James Carman
- * @since 1.0
- */
-public interface DuplicateEcho
+import java.lang.annotation.Annotation;
+
+class AnnotationTypeTrainer<R extends Annotation> extends AnnotationTrainer<R>
 {
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
 
-    public String echoBack( String message );
-}
+    AnnotationTypeTrainer(Class<R> annotationType)
+    {
+        super(annotationType);
+    }
+
+    @Override
+    protected void train(R trainee)
+    {
+        when(trainee.annotationType()).thenReturn(traineeType);
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/BaseAnnotationTrainer.java b/core/src/main/java/org/apache/commons/proxy2/stub/BaseAnnotationTrainer.java
new file mode 100644
index 0000000..2a23fcf
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/BaseAnnotationTrainer.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.commons.proxy2.interceptor.InterceptorUtils;
+
+public abstract class BaseAnnotationTrainer<S extends BaseAnnotationTrainer<S, A>, A extends Annotation> extends
+        BaseTrainer<S, A>
+{
+    protected BaseAnnotationTrainer()
+    {
+        super();
+    }
+
+    protected BaseAnnotationTrainer(Class<A> traineeType)
+    {
+        super(traineeType);
+    }
+
+    protected class WhenAnnotation<R> extends WhenObject<R>
+    {
+        public S thenStub(Class<R> type)
+        {
+            trainingContext().push(type);
+            trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
+            return self();
+        }
+
+        @Override
+        public S thenStub(BaseTrainer<?, R> trainer)
+        {
+            final R trainee = trainingContext().push(trainer.traineeType);
+            trainer.train(trainee);
+            trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
+            return self();
+        }
+    }
+
+    protected class WhenAnnotationArray<R> extends WhenObjectArray<R>
+    {
+        protected WhenAnnotationArray(Class<? extends R> componentType)
+        {
+            super(componentType);
+        }
+
+        @Override
+        public StubAnnotationArrayBuilder<R> thenBuildArray()
+        {
+            return new StubAnnotationArrayBuilder<R>(componentType);
+        }
+    }
+
+    protected class StubAnnotationArrayBuilder<R> extends StubArrayBuilder<R>
+    {
+        private final BaseTrainer<?, R> annotationTypeTrainer;
+
+        private <N extends Annotation> StubAnnotationArrayBuilder(final Class<? extends R> componentType)
+        {
+            super(componentType);
+            @SuppressWarnings("unchecked")
+            final Class<N> annotationType = (Class<N>) componentType;
+            @SuppressWarnings("unchecked")
+            final BaseTrainer<?, R> annotationTypeTrainer = (BaseTrainer<?, R>) new AnnotationTypeTrainer<N>(
+                    annotationType);
+            this.annotationTypeTrainer = annotationTypeTrainer;
+        }
+
+        @Override
+        public StubAnnotationArrayBuilder<R> addElement(BaseTrainer<?, R> trainer)
+        {
+            final R trainee = trainingContext().push(trainer.traineeType);
+
+            annotationTypeTrainer.train(trainee);
+            trainer.train(trainee);
+
+            elements.add(trainingContext().<R> pop());
+            return this;
+        }
+    }
+
+    @Override
+    public <R> WhenAnnotation<R> when(R expression)
+    {
+        return new WhenAnnotation<R>();
+    }
+
+    @Override
+    public <R> WhenAnnotationArray<R> when(R[] expression)
+    {
+        @SuppressWarnings("unchecked")
+        final Class<? extends R> componentType = (Class<? extends R>) expression.getClass().getComponentType();
+        return new WhenAnnotationArray<R>(componentType);
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/BaseTrainer.java b/core/src/main/java/org/apache/commons/proxy2/stub/BaseTrainer.java
new file mode 100644
index 0000000..8c386da
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/BaseTrainer.java
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.lang3.reflect.TypeUtils;
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.ObjectProvider;
+import org.apache.commons.proxy2.interceptor.InterceptorUtils;
+import org.apache.commons.proxy2.interceptor.matcher.ArgumentMatcher;
+import org.apache.commons.proxy2.interceptor.matcher.argument.ArgumentMatcherUtils;
+
+public abstract class BaseTrainer<S extends BaseTrainer<S, T>, T>
+{
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Fields
+    // ----------------------------------------------------------------------------------------------------------------------
+    public final Class<T> traineeType;
+
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Constructors
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    /**
+     * Create a new {@link BaseTrainer} instance. This constructor should only
+     * be called by classes that explicitly assign the T parameter in the class
+     * definition. This should include basically any runtime-usable class.
+     */
+    protected BaseTrainer()
+    {
+        this(null);
+    }
+
+    protected BaseTrainer(Class<T> traineeType)
+    {
+        super();
+        if (traineeType != null)
+        {
+            this.traineeType = traineeType;
+            return;
+        }
+        @SuppressWarnings("unchecked")
+        final Class<T> resolvedVariable = (Class<T>) TypeUtils.getRawType(BaseTrainer.class.getTypeParameters()[1],
+                getClass());
+        Validate.isTrue(resolvedVariable != null, "Trainee type was not specified and could not be calculated for %s",
+                getClass());
+        this.traineeType = resolvedVariable;
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Abstract Methods
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    protected abstract void train(T trainee);
+
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Other Methods
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    protected <R> R any(Class<R> type)
+    {
+        return argThat(ArgumentMatcherUtils.<R> any());
+    }
+
+    protected <R> R eq(R value)
+    {
+        return argThat(ArgumentMatcherUtils.eq(value));
+    }
+
+    protected <R> R isInstance(Class<R> type)
+    {
+        return argThat(ArgumentMatcherUtils.<R> isA(type));
+    }
+
+    protected <R> R argThat(ArgumentMatcher<R> matcher)
+    {
+        trainingContext().record(matcher);
+        return null;
+    }
+
+    protected void thenThrow(Exception e)
+    {
+        trainingContext().then(InterceptorUtils.throwing(e));
+    }
+
+    protected void thenThrow(ObjectProvider<? extends Exception> provider)
+    {
+        trainingContext().then(InterceptorUtils.throwing(provider));
+    }
+
+    protected TrainingContext trainingContext()
+    {
+        return TrainingContext.current();
+    }
+
+    public <R> WhenObject<R> when(R expression)
+    {
+        return new WhenObject<R>();
+    }
+
+    public WhenClass when(Class<?> expression)
+    {
+        return new WhenClass();
+    }
+
+    public WhenByteArray when(byte[] expression)
+    {
+        return new WhenByteArray();
+    }
+
+    public WhenBooleanArray when(boolean[] expression)
+    {
+        return new WhenBooleanArray();
+    }
+
+    public WhenIntArray when(int[] expression)
+    {
+        return new WhenIntArray();
+    }
+
+    public WhenShortArray when(short[] expresssion)
+    {
+        return new WhenShortArray();
+    }
+
+    public WhenLongArray when(long[] expression)
+    {
+        return new WhenLongArray();
+    }
+
+    public WhenFloatArray when(float[] expression)
+    {
+        return new WhenFloatArray();
+    }
+
+    public WhenDoubleArray when(double[] expression)
+    {
+        return new WhenDoubleArray();
+    }
+
+    public <R> WhenObjectArray<R> when(R[] expression)
+    {
+        @SuppressWarnings("unchecked")
+        final Class<? extends R> componentType = (Class<? extends R>) expression.getClass().getComponentType();
+        return new WhenObjectArray<R>(componentType);
+    }
+
+    public WhenCharArray when(char[] expression)
+    {
+        return new WhenCharArray();
+    }
+
+    @SuppressWarnings("unchecked")
+    protected S self()
+    {
+        return (S) this;
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Inner Classes
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    protected abstract class BaseWhen<R>
+    {
+        public S thenThrow(Exception e)
+        {
+            return then(InterceptorUtils.throwing(e));
+        }
+
+        public S thenThrow(ObjectProvider<? extends Exception> provider)
+        {
+            return then(InterceptorUtils.throwing(provider));
+        }
+
+        public S thenAnswer(ObjectProvider<? extends R> provider)
+        {
+            return then(InterceptorUtils.provider(provider));
+        }
+
+        public S then(Interceptor interceptor)
+        {
+            trainingContext().then(interceptor);
+            return self();
+        }
+    }
+
+    protected class WhenBooleanArray extends BaseWhen<boolean[]>
+    {
+        public S thenReturn(boolean... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+
+    protected class WhenByteArray extends BaseWhen<byte[]>
+    {
+        public S thenReturn(byte... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+
+    protected class WhenCharArray extends BaseWhen<char[]>
+    {
+        public S thenReturn(char... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+
+    protected class WhenDoubleArray extends BaseWhen<double[]>
+    {
+        public S thenReturn(double... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+
+    protected class WhenFloatArray extends BaseWhen<float[]>
+    {
+        public S thenReturn(float... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+
+    protected class WhenIntArray extends BaseWhen<int[]>
+    {
+        public S thenReturn(int... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+
+    protected class WhenLongArray extends BaseWhen<long[]>
+    {
+        public S thenReturn(long... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+
+    protected class WhenObject<R> extends BaseWhen<R>
+    {
+        public S thenReturn(R value)
+        {
+            trainingContext().then(InterceptorUtils.constant(value));
+            return self();
+        }
+
+        public S thenStub(BaseTrainer<?, R> trainer)
+        {
+            final R trainee = trainingContext().push(trainer.traineeType);
+            trainer.train(trainee);
+            trainingContext().then(InterceptorUtils.constant(trainingContext().pop()));
+            return self();
+        }
+    }
+
+    /**
+     * Intermediate result of a when(Class) call. Provided because it is such a
+     * common case to have a mismatch between a declared Class<?> return type
+     * and the bound parameter of a class literal.
+     */
+    protected class WhenClass extends BaseWhen<Class<?>>
+    {
+        public S thenReturn(Class<?> value)
+        {
+            trainingContext().then(InterceptorUtils.constant(value));
+            return self();
+        }
+    }
+
+    protected class WhenObjectArray<R> extends BaseWhen<R[]>
+    {
+        protected final Class<? extends R> componentType;
+
+        protected WhenObjectArray(Class<? extends R> componentType)
+        {
+            this.componentType = componentType;
+        }
+
+        public S thenReturn(R... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+
+        public StubArrayBuilder<R> thenBuildArray()
+        {
+            return new StubArrayBuilder<R>(componentType);
+        }
+    }
+
+    protected class StubArrayBuilder<R>
+    {
+        protected final List<R> elements = new ArrayList<R>();
+        protected final Class<? extends R> componentType;
+
+        protected StubArrayBuilder(Class<? extends R> componentType)
+        {
+            this.componentType = componentType;
+        }
+
+        public StubArrayBuilder<R> addElement(BaseTrainer<?, R> trainer)
+        {
+            final R trainee = trainingContext().push(trainer.traineeType);
+            trainer.train(trainee);
+            elements.add(trainingContext().<R> pop());
+            return this;
+        }
+
+        public S build()
+        {
+            @SuppressWarnings("unchecked")
+            final R[] array = elements.toArray((R[]) Array.newInstance(componentType, elements.size()));
+            trainingContext().then(InterceptorUtils.constant(array));
+            return self();
+        }
+    }
+
+    protected class WhenShortArray extends BaseWhen<short[]>
+    {
+        public S thenReturn(short... values)
+        {
+            trainingContext().then(InterceptorUtils.constant(ArrayUtils.clone(values)));
+            return self();
+        }
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/StubBuilder.java b/core/src/main/java/org/apache/commons/proxy2/stub/StubBuilder.java
new file mode 100644
index 0000000..8b3b261
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/StubBuilder.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.lang3.builder.Builder;
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ObjectProvider;
+import org.apache.commons.proxy2.ProxyFactory;
+import org.apache.commons.proxy2.interceptor.SwitchInterceptor;
+import org.apache.commons.proxy2.invoker.NullInvoker;
+import org.apache.commons.proxy2.provider.ConstantProvider;
+
+public class StubBuilder<T> implements Builder<T>
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private final ProxyFactory proxyFactory;
+    private final T target;
+    private final SwitchInterceptor switchInterceptor = new SwitchInterceptor();
+    private final Set<Class<?>> proxyTypes = new HashSet<Class<?>>();
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public StubBuilder(ProxyFactory proxyFactory, Class<T> type)
+    {
+        this(proxyFactory, type, NullInvoker.INSTANCE);
+    }
+
+    public StubBuilder(ProxyFactory proxyFactory, Class<T> type, Invoker invoker)
+    {
+        this.proxyFactory = proxyFactory;
+        this.target = proxyFactory.createInvokerProxy(invoker, type);
+        this.proxyTypes.add(Validate.notNull(type));
+    }
+    
+    public StubBuilder(ProxyFactory proxyFactory, Class<T> type, ObjectProvider<? extends T> provider)
+    {
+        this.proxyFactory = proxyFactory;
+        this.target = proxyFactory.createDelegatorProxy(provider, type);
+        this.proxyTypes.add(Validate.notNull(type));
+    }
+
+    public StubBuilder(ProxyFactory proxyFactory, Class<T> type, T target)
+    {
+        this.proxyFactory = proxyFactory;
+        this.target = proxyFactory.createDelegatorProxy(new ConstantProvider<T>(target), type);
+        this.proxyTypes.add(Validate.notNull(type));
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    public T build()
+    {
+        return proxyFactory.createInterceptorProxy(target, switchInterceptor,
+                proxyTypes.toArray(ArrayUtils.EMPTY_CLASS_ARRAY));
+    }
+
+    public <O> StubBuilder<T> train(BaseTrainer<?, O> trainer)
+    {
+        final TrainingContext trainingContext = TrainingContext.join(proxyFactory);
+        try
+        {
+            final O trainee = trainingContext.push(trainer.traineeType, switchInterceptor);
+            trainer.train(trainee);
+            proxyTypes.add(trainer.traineeType);
+        }
+        finally
+        {
+            trainingContext.part();
+        }
+        return this;
+    }
+
+    public StubBuilder<T> addProxyTypes(Class<?>... proxyTypes)
+    {
+        Collections.addAll(this.proxyTypes, Validate.noNullElements(proxyTypes));
+        return this;
+    }
+}
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java b/core/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java
new file mode 100644
index 0000000..dff59f9
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/StubInterceptorBuilder.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.ProxyFactory;
+import org.apache.commons.proxy2.interceptor.SwitchInterceptor;
+
+public class StubInterceptorBuilder
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private final ProxyFactory proxyFactory;
+    private final SwitchInterceptor interceptor = new SwitchInterceptor();
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public StubInterceptorBuilder(ProxyFactory proxyFactory)
+    {
+        this.proxyFactory = proxyFactory;
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    public Interceptor build()
+    {
+        return interceptor;
+    }
+
+    public <T> StubInterceptorBuilder train(Trainer<T> trainer)
+    {
+        final TrainingContext trainingContext = TrainingContext.join(proxyFactory);
+        try
+        {
+            final T stub = trainingContext.push(trainer.traineeType, interceptor);
+            trainer.train(stub);
+        }
+        finally
+        {
+            trainingContext.part();
+        }
+        return this;
+    }
+}
diff --git a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java b/core/src/main/java/org/apache/commons/proxy2/stub/Trainer.java
similarity index 65%
copy from src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
copy to core/src/main/java/org/apache/commons/proxy2/stub/Trainer.java
index f73551d..d0962bc 100644
--- a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/Trainer.java
@@ -15,17 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.util;
+package org.apache.commons.proxy2.stub;
 
-/**
- * @author James Carman
- * @since 1.0
- */
-public interface DuplicateEcho
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
+public abstract class Trainer<T> extends BaseTrainer<Trainer<T>, T> {
 
-    public String echoBack( String message );
+    protected Trainer() {
+        super();
+    }
+
+    protected Trainer(Class<T> traineeType) {
+        super(traineeType);
+    }
+
 }
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java b/core/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java
new file mode 100644
index 0000000..c0ad666
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/TrainingContext.java
@@ -0,0 +1,257 @@
+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.Array;
+import java.lang.reflect.Method;
+import java.util.*;
+
+class TrainingContext
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private static final ThreadLocal<TrainingContext> TRAINING_CONTEXT = new ThreadLocal<TrainingContext>();
+
+    private final ProxyFactory proxyFactory;
+
+    private Deque<TrainingContextFrame<?>> frameDeque = new LinkedList<TrainingContextFrame<?>>();
+
+    private final TrainingContext resume;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Static Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    static TrainingContext current()
+    {
+        return TRAINING_CONTEXT.get();
+    }
+
+    static synchronized TrainingContext join(ProxyFactory proxyFactory)
+    {
+        final TrainingContext context = new TrainingContext(proxyFactory);
+        TRAINING_CONTEXT.set(context);
+        return context;
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    private TrainingContext(ProxyFactory proxyFactory)
+    {
+        this.proxyFactory = proxyFactory;
+        this.resume = current();
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    void part()
+    {
+        synchronized (TRAINING_CONTEXT)
+        {
+            if (resume == null)
+            {
+                TRAINING_CONTEXT.remove();
+            }
+            else
+            {
+                TRAINING_CONTEXT.set(resume);
+            }
+        }
+    }
+
+    private TrainingContextFrame<?> peek()
+    {
+        return frameDeque.peek();
+    }
+
+    <T> T pop()
+    {
+        return pop(NullInvoker.INSTANCE);
+    }
+
+    <T> T pop(Invoker invoker)
+    {
+        final TrainingContextFrame<?> frame = frameDeque.pop();
+        return proxyFactory.createInterceptorProxy(
+                proxyFactory.createInvokerProxy(invoker, frame.type),
+                frame.stubInterceptor,
+                frame.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>(type, switchInterceptor);
+        Invoker invoker = new TrainingInvoker(frame);
+        frameDeque.push(frame);
+        return proxyFactory.createInvokerProxy(invoker, type);
+    }
+
+    void record(ArgumentMatcher<?> argumentMatcher)
+    {
+        peek().argumentMatchers.add(argumentMatcher);
+    }
+
+    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];
+                @SuppressWarnings({ "rawtypes", "unchecked" })
+                final boolean matches = ((ArgumentMatcher) matchers[i]).matches(argument);
+                if (!matches)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private static final 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 final Class<T> type;
+
+        private TrainingContextFrame(Class<T> type, SwitchInterceptor stubInterceptor)
+        {
+            this.type = type;
+            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 final class TrainingInvoker implements Invoker
+    {
+        private static final long serialVersionUID = 1L;
+
+        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 = current().peek();
+            if (!frame.getId().equals(id))
+            {
+                throw new IllegalStateException("Wrong stub!");
+            }
+            else
+            {
+                frame.methodInvoked(method, arguments);
+            }
+
+            final Class<?> type = method.getReturnType();
+
+            if (Object[].class.isAssignableFrom(type))
+            {
+                return Array.newInstance(type.getComponentType(), 0);
+            }
+            return ProxyUtils.nullValue(type);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java b/core/src/main/java/org/apache/commons/proxy2/stub/package-info.java
similarity index 65%
copy from src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
copy to core/src/main/java/org/apache/commons/proxy2/stub/package-info.java
index f73551d..4bcf681 100644
--- a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/package-info.java
@@ -15,17 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.util;
-
 /**
- * @author James Carman
- * @since 1.0
+ * Contains mechanisms for stubbing behavior on {@link org.apache.commons.proxy2.ProxyFactory}-generated proxy objects.
  */
-public interface DuplicateEcho
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public String echoBack( String message );
-}
+package org.apache.commons.proxy2.stub;
\ No newline at end of file
diff --git a/core/src/test/java/org/apache/commons/proxy2/AbstractProxyFactoryTestCase.java b/core/src/test/java/org/apache/commons/proxy2/AbstractProxyFactoryTestCase.java
new file mode 100644
index 0000000..9a65855
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/AbstractProxyFactoryTestCase.java
@@ -0,0 +1,396 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+import org.apache.commons.proxy2.provider.BeanProvider;

+import org.apache.commons.proxy2.provider.ConstantProvider;

+import org.apache.commons.proxy2.provider.SingletonProvider;

+import org.apache.commons.proxy2.util.*;

+import org.junit.Test;

+

+import java.io.IOException;

+import java.io.Serializable;

+import java.lang.reflect.Method;

+import java.util.*;

+

+import static org.junit.Assert.*;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+@SuppressWarnings("serial")

+public abstract class AbstractProxyFactoryTestCase extends AbstractTestCase

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private static final Class<?>[] ECHO_ONLY = new Class[]{Echo.class};

+    protected final ProxyFactory factory;

+    private static final Class<?>[] COMPARABLE_ONLY = new Class[]{Comparable.class};

+

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+

+    protected AbstractProxyFactoryTestCase()

+    {

+        final ServiceLoader<ProxyFactory> serviceLoader = ServiceLoader.load(ProxyFactory.class);

+        Iterator<ProxyFactory> iter = serviceLoader.iterator();

+        if (iter.hasNext())

+        {

+            this.factory = iter.next();

+        }

+        else

+        {

+            throw new RuntimeException("Unable to find proxy factory implementation.");

+        }

+

+    }

+

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    private ObjectProvider<Echo> createSingletonEcho()

+    {

+        return new SingletonProvider<Echo>(new BeanProvider<Echo>(EchoImpl.class));

+    }

+

+    @Test

+    public void testInterceptorHashCode()

+    {

+        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);

+        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));

+    }

+

+    @Test

+    public void testInvokerHashCode() throws Exception

+    {

+        final Echo proxy = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);

+        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));

+    }

+

+    @Test

+    public void testDelegatorHashCode() throws Exception

+    {

+        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));

+    }

+

+    @Test

+    public void testInterceptorEquals()

+    {

+        final Date date = new Date();

+        final Comparable<?> proxy1 = factory.createInterceptorProxy(date,

+                new NoOpMethodInterceptor(), COMPARABLE_ONLY);

+        final Comparable<?> proxy2 = factory.createInterceptorProxy(date,

+                new NoOpMethodInterceptor(), COMPARABLE_ONLY);

+        assertEquals(proxy1, proxy1);

+        assertFalse(proxy1.equals(proxy2));

+        assertFalse(proxy2.equals(proxy1));

+    }

+

+    @Test

+    public void testInvokerEquals() throws Exception

+    {

+        final Comparable<?> proxy1 = factory.createInvokerProxy(new InvokerTester(), COMPARABLE_ONLY);

+        final Comparable<?> proxy2 = factory.createInvokerProxy(new InvokerTester(), COMPARABLE_ONLY);

+        assertEquals(proxy1, proxy1);

+        assertFalse(proxy1.equals(proxy2));

+        assertFalse(proxy2.equals(proxy1));

+    }

+

+    @Test

+    public void testDelegatorEquals() throws Exception

+    {

+        final Date date = new Date();

+        final Comparable<?> proxy1 = factory.createDelegatorProxy(new ConstantProvider<Date>(date),

+                COMPARABLE_ONLY);

+        final Comparable<?> proxy2 = factory.createDelegatorProxy(new ConstantProvider<Date>(date),

+                COMPARABLE_ONLY);

+        assertEquals(proxy1, proxy1);

+        assertFalse(proxy1.equals(proxy2));

+        assertFalse(proxy2.equals(proxy1));

+    }

+

+    @Test

+    public void testBooleanInterceptorParameter()

+    {

+        final Echo echo = factory.createInterceptorProxy(new EchoImpl(), new InterceptorTester(), ECHO_ONLY);

+        assertFalse(echo.echoBack(false));

+        assertTrue(echo.echoBack(true));

+    }

+

+    @Test

+    public void testCanProxy()

+    {

+        assertTrue(factory.canProxy(Echo.class));

+        assertFalse(factory.canProxy(EchoImpl.class));

+    }

+

+    @Test

+    public void testChangingArguments()

+    {

+        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new ChangeArgumentInterceptor(), ECHO_ONLY);

+        assertEquals("something different", proxy.echoBack("whatever"));

+    }

+

+    @Test

+    public void testCreateDelegatingProxy()

+    {

+        final Echo echo = factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);

+        echo.echo();

+        assertEquals("message", echo.echoBack("message"));

+        assertEquals("ab", echo.echoBack("a", "b"));

+    }

+

+    @Test

+    public void testCreateInterceptorProxy()

+    {

+        final Echo target = factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);

+        final Echo proxy = factory.createInterceptorProxy(target, new SuffixInterceptor(" suffix"), ECHO_ONLY);

+        proxy.echo();

+        assertEquals("message suffix", proxy.echoBack("message"));

+    }

+

+    @Test

+    public void testDelegatingProxyClassCaching() throws Exception

+    {

+        final Echo proxy1 = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        final Echo proxy2 = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        assertNotSame(proxy1, proxy2);

+        assertSame(proxy1.getClass(), proxy2.getClass());

+    }

+

+    @Test

+    public void testDelegatingProxyInterfaceOrder()

+    {

+        final Echo echo = factory.createDelegatorProxy(createSingletonEcho(), Echo.class, DuplicateEcho.class);

+        final List<Class<?>> expected = new LinkedList<Class<?>>(Arrays.<Class<?>>asList(Echo.class, DuplicateEcho.class));

+        final List<Class<?>> actual = new LinkedList<Class<?>>(Arrays.asList(echo.getClass().getInterfaces()));

+        actual.retainAll(expected);  // Doesn't alter order!

+        assertEquals(expected, actual);

+    }

+

+    @Test

+    public void testDelegatingProxySerializable() throws Exception

+    {

+        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        assertSerializable(proxy);

+    }

+

+    @Test

+    public void testInterceptingProxyClassCaching() throws Exception

+    {

+        final Echo proxy1 = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);

+        final Echo proxy2 = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);

+        assertNotSame(proxy1, proxy2);

+        assertSame(proxy1.getClass(), proxy2.getClass());

+    }

+

+    @Test

+    public void testInterceptingProxySerializable() throws Exception

+    {

+        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);

+        assertSerializable(proxy);

+    }

+

+    @Test(expected = IOException.class)

+    public void testInterceptorProxyWithCheckedException() throws Exception

+    {

+        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);

+        proxy.ioException();

+    }

+

+    @Test(expected = IllegalArgumentException.class)

+    public void testInterceptorProxyWithUncheckedException() throws Exception

+    {

+        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);

+        proxy.illegalArgument();

+    }

+

+    @Test

+    public void testInterfaceHierarchies()

+    {

+        final SortedSet<String> set = factory.createDelegatorProxy(new ConstantProvider<SortedSet<String>>(new TreeSet<String>()), SortedSet.class);

+        set.add("Hello");

+    }

+

+    @Test

+    public void testInvokerProxy() throws Exception

+    {

+        final InvokerTester tester = new InvokerTester();

+        final Echo echo = factory.createInvokerProxy(tester, ECHO_ONLY);

+        echo.echoBack("hello");

+        assertEquals(Echo.class.getMethod("echoBack", String.class), tester.method);

+        assertSame(echo, tester.proxy);

+        assertNotNull(tester.args);

+        assertEquals(1, tester.args.length);

+        assertEquals("hello", tester.args[0]);

+    }

+

+    @Test

+    public void testInvokerProxyClassCaching() throws Exception

+    {

+        final Echo proxy1 = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);

+        final Echo proxy2 = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);

+        assertNotSame(proxy1, proxy2);

+        assertSame(proxy1.getClass(), proxy2.getClass());

+    }

+

+    @Test

+    public void testInvokerProxySerializable() throws Exception

+    {

+        final Echo proxy = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);

+        assertSerializable(proxy);

+    }

+

+    @Test

+    public void testMethodInvocationClassCaching() throws Exception

+    {

+        final InterceptorTester tester = new InterceptorTester();

+        final EchoImpl target = new EchoImpl();

+        final Echo proxy1 = factory.createInterceptorProxy(target, tester, ECHO_ONLY);

+        final Echo proxy2 = factory.createInterceptorProxy(target, tester, Echo.class, DuplicateEcho.class);

+        proxy1.echoBack("hello1");

+        final Class<?> invocationClass1 = tester.invocationClass;

+        proxy2.echoBack("hello2");

+        assertSame(invocationClass1, tester.invocationClass);

+    }

+

+    @Test

+    public void testMethodInvocationDuplicateMethods() throws Exception

+    {

+        final InterceptorTester tester = new InterceptorTester();

+        final EchoImpl target = new EchoImpl();

+        final Echo proxy = factory.createInterceptorProxy(target, tester, Echo.class, DuplicateEcho.class);

+        proxy.echoBack("hello");

+        assertEquals(Echo.class.getMethod("echoBack", String.class), tester.method);

+    }

+

+    @Test

+    public void testMethodInvocationImplementation() throws Exception

+    {

+        final InterceptorTester tester = new InterceptorTester();

+        final EchoImpl target = new EchoImpl();

+        final Echo proxy = factory.createInterceptorProxy(target, tester, ECHO_ONLY);

+        proxy.echo();

+        assertNotNull(tester.arguments);

+        assertEquals(0, tester.arguments.length);

+        assertEquals(Echo.class.getMethod("echo"), tester.method);

+        assertSame(proxy, tester.proxy);

+        proxy.echoBack("Hello");

+        assertNotNull(tester.arguments);

+        assertEquals(1, tester.arguments.length);

+        assertEquals("Hello", tester.arguments[0]);

+        assertEquals(Echo.class.getMethod("echoBack", String.class), tester.method);

+        proxy.echoBack("Hello", "World");

+        assertNotNull(tester.arguments);

+        assertEquals(2, tester.arguments.length);

+        assertEquals("Hello", tester.arguments[0]);

+        assertEquals("World", tester.arguments[1]);

+        assertEquals(Echo.class.getMethod("echoBack", String.class, String.class), tester.method);

+    }

+

+    @Test

+    public void testPrimitiveParameter()

+    {

+        final Echo echo = factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);

+        assertEquals(1, echo.echoBack(1));

+    }

+

+    @Test(expected = IOException.class)

+    public void testProxyWithCheckedException() throws Exception

+    {

+        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        proxy.ioException();

+    }

+

+    @Test(expected = IllegalArgumentException.class)

+    public void testProxyWithUncheckedException() throws Exception

+    {

+        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        proxy.illegalArgument();

+    }

+

+    @Test

+    public void testWithNonAccessibleTargetType()

+    {

+        final Echo proxy = factory.createInterceptorProxy(new PrivateEcho(), new NoOpMethodInterceptor(), ECHO_ONLY);

+        proxy.echo();

+    }

+

+//**********************************************************************************************************************

+// Inner Classes

+//**********************************************************************************************************************

+

+    private static class ChangeArgumentInterceptor implements Interceptor

+    {

+        public Object intercept(Invocation methodInvocation) throws Throwable

+        {

+            methodInvocation.getArguments()[0] = "something different";

+            return methodInvocation.proceed();

+        }

+    }

+

+    protected static class InterceptorTester implements Interceptor

+    {

+        private Object[] arguments;

+        private Method method;

+        private Object proxy;

+        private Class<?> invocationClass;

+

+        public Object intercept(Invocation methodInvocation) throws Throwable

+        {

+            arguments = methodInvocation.getArguments();

+            method = methodInvocation.getMethod();

+            proxy = methodInvocation.getProxy();

+            invocationClass = methodInvocation.getClass();

+            return methodInvocation.proceed();

+        }

+    }

+

+    protected static class InvokerTester implements Invoker

+    {

+        private Object method;

+        private Object[] args;

+        private Object proxy;

+

+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

+        {

+            this.proxy = proxy;

+            this.method = method;

+            this.args = args;

+            return null;

+        }

+    }

+

+    protected static class NoOpMethodInterceptor implements Interceptor, Serializable

+    {

+        public Object intercept(Invocation methodInvocation) throws Throwable

+        {

+            return methodInvocation.proceed();

+        }

+    }

+

+    private static class PrivateEcho extends EchoImpl

+    {

+    }

+}

diff --git a/core/src/test/java/org/apache/commons/proxy2/AbstractSubclassingProxyFactoryTestCase.java b/core/src/test/java/org/apache/commons/proxy2/AbstractSubclassingProxyFactoryTestCase.java
new file mode 100644
index 0000000..7146ae5
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/AbstractSubclassingProxyFactoryTestCase.java
@@ -0,0 +1,214 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;

+

+import org.apache.commons.proxy2.exception.ProxyFactoryException;

+import org.apache.commons.proxy2.invoker.NullInvoker;

+import org.apache.commons.proxy2.provider.ConstantProvider;

+import org.apache.commons.proxy2.util.AbstractEcho;

+import org.apache.commons.proxy2.util.Echo;

+import org.apache.commons.proxy2.util.EchoImpl;

+import org.junit.Test;

+

+import java.util.Date;

+

+import static org.junit.Assert.*;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+@SuppressWarnings("serial")

+public abstract class AbstractSubclassingProxyFactoryTestCase extends AbstractProxyFactoryTestCase

+{

+//----------------------------------------------------------------------------------------------------------------------

+// Fields

+//----------------------------------------------------------------------------------------------------------------------

+

+    private static final Class<?>[] DATE_ONLY = new Class[]{Date.class};

+

+//----------------------------------------------------------------------------------------------------------------------

+// Other Methods

+//----------------------------------------------------------------------------------------------------------------------

+

+    @Test

+    public void testCanProxy()

+    {

+        assertTrue(factory.canProxy(new Class[]{Echo.class}));

+        assertTrue(factory.canProxy(new Class[]{EchoImpl.class}));

+        assertFalse(factory.canProxy(new Class[]{FinalEcho.class}));

+        assertTrue(factory.canProxy(new Class[]{FinalMethodEcho.class, Echo.class}));

+        assertFalse(factory.canProxy(new Class[]{NoDefaultConstructorEcho.class}));

+        assertTrue(factory.canProxy(new Class[]{ProtectedConstructorEcho.class}));

+        assertFalse(factory.canProxy(new Class[]{InvisibleEcho.class}));

+        assertFalse(factory.canProxy(new Class[]{Echo.class, EchoImpl.class, String.class}));

+    }

+

+    @Test

+    public void testDelegatorEquals() throws Exception

+    {

+        final EqualsEcho echo = new EqualsEcho("text");

+        final Echo proxy1 = factory.createDelegatorProxy(new ConstantProvider<Echo>(echo),

+                new Class[]{EqualsEcho.class});

+        final Echo proxy2 = factory.createDelegatorProxy(new ConstantProvider<Echo>(echo),

+                new Class[]{EqualsEcho.class});

+        assertEquals(proxy1, proxy1);

+        assertFalse(proxy1.equals(proxy2));

+        assertFalse(proxy2.equals(proxy1));

+    }

+

+    @Test(expected = ProxyFactoryException.class)

+    public void testDelegatorWithMultipleSuperclasses()

+    {

+        factory.createDelegatorProxy(new ConstantProvider<EchoImpl>(new EchoImpl()),

+                new Class[]{EchoImpl.class, String.class});

+    }

+

+    @Test

+    public void testDelegatorWithSuperclass()

+    {

+        final Echo echo = factory

+                .createDelegatorProxy(new ConstantProvider<EchoImpl>(new EchoImpl()), new Class[]{Echo.class, EchoImpl.class});

+        assertTrue(echo instanceof EchoImpl);

+    }

+

+    @Test

+    public void testInterceptorEquals()

+    {

+        final EqualsEcho echo = new EqualsEcho("text");

+        final Echo proxy1 = factory.createInterceptorProxy(echo,

+                new NoOpMethodInterceptor(), new Class[]{EqualsEcho.class});

+        final Echo proxy2 = factory.createInterceptorProxy(echo,

+                new NoOpMethodInterceptor(), new Class[]{EqualsEcho.class});

+        assertEquals(proxy1, proxy1);

+        assertFalse(proxy1.equals(proxy2));

+        assertFalse(proxy2.equals(proxy1));

+    }

+

+    @Test(expected = ProxyFactoryException.class)

+    public void testInterceptorWithMultipleSuperclasses()

+    {

+        factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(),

+                new Class[]{EchoImpl.class, String.class});

+    }

+

+    @Test

+    public void testInterceptorWithSuperclass()

+    {

+        final Echo echo = factory

+                .createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), new Class[]{Echo.class, EchoImpl.class});

+        assertTrue(echo instanceof EchoImpl);

+    }

+

+    @Test(expected = ProxyFactoryException.class)

+    public void testInvocationHandlerWithMultipleSuperclasses()

+    {

+        factory.createInvokerProxy(new NullInvoker(),

+                new Class[]{EchoImpl.class, String.class});

+    }

+

+    @Test

+    public void testInvokerEquals() throws Exception

+    {

+        final Date proxy1 = factory.createInvokerProxy(new InvokerTester(), DATE_ONLY);

+        final Date proxy2 = factory.createInvokerProxy(new InvokerTester(), DATE_ONLY);

+        assertEquals(proxy1, proxy1);

+        assertFalse(proxy1.equals(proxy2));

+        assertFalse(proxy2.equals(proxy1));

+    }

+

+    @Test

+    public void testInvokerWithSuperclass()

+    {

+        final Echo echo = factory

+                .createInvokerProxy(new NullInvoker(), new Class[]{Echo.class, EchoImpl.class});

+        assertTrue(echo instanceof EchoImpl);

+    }

+

+    @Test

+    public void testProxiesWithClashingFinalMethodInSuperclass()

+    {

+        final Class<?>[] proxyClasses = new Class[]{Echo.class, FinalMethodEcho.class};

+        Echo proxy = factory.createDelegatorProxy(new ConstantProvider<EchoImpl>(new EchoImpl()), proxyClasses);

+        assertEquals("final", proxy.echoBack("echo"));

+

+        proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), proxyClasses);

+        assertEquals("final", proxy.echoBack("echo"));

+

+        proxy = factory.createInvokerProxy(new NullInvoker(), proxyClasses);

+        assertEquals("final", proxy.echoBack("echo"));

+    }

+

+    @Test

+    public void testWithAbstractSuperclass()

+    {

+        final Echo echo = factory.createDelegatorProxy(new ConstantProvider<EchoImpl>(new EchoImpl()), new Class[]{AbstractEcho.class});

+        assertEquals("hello", echo.echoBack("hello"));

+        assertEquals("helloworld", echo.echoBack("hello", "world"));

+    }

+

+//----------------------------------------------------------------------------------------------------------------------

+// Inner Classes

+//----------------------------------------------------------------------------------------------------------------------

+

+    public static class EqualsEcho extends EchoImpl

+    {

+        @SuppressWarnings("unused")

+        private final String text;

+

+        protected EqualsEcho()

+        {

+            this("testing");

+        }

+

+        public EqualsEcho(String text)

+        {

+            this.text = text;

+        }

+    }

+

+    public static final class FinalEcho extends EchoImpl

+    {

+    }

+

+    public static class FinalMethodEcho extends EchoImpl

+    {

+        public final String echoBack(String message)

+        {

+            return "final";

+        }

+    }

+

+    private static class InvisibleEcho extends EchoImpl

+    {

+    }

+

+    public static class NoDefaultConstructorEcho extends EchoImpl

+    {

+        public NoDefaultConstructorEcho(String param)

+        {

+        }

+    }

+

+    public static class ProtectedConstructorEcho extends EchoImpl

+    {

+        protected ProtectedConstructorEcho()

+        {

+        }

+    }

+}

diff --git a/src/test/java/org/apache/commons/proxy/TestProxyUtils.java b/core/src/test/java/org/apache/commons/proxy2/ProxyUtilsTest.java
similarity index 67%
rename from src/test/java/org/apache/commons/proxy/TestProxyUtils.java
rename to core/src/test/java/org/apache/commons/proxy2/ProxyUtilsTest.java
index 5dcf0ff..e2f25f1 100644
--- a/src/test/java/org/apache/commons/proxy/TestProxyUtils.java
+++ b/core/src/test/java/org/apache/commons/proxy2/ProxyUtilsTest.java
@@ -15,19 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy;
+package org.apache.commons.proxy2;
 
-import junit.framework.TestCase;
-import org.apache.commons.proxy.factory.javassist.JavassistProxyFactory;
-import org.apache.commons.proxy.util.DuplicateEcho;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.DuplicateEcho;
+import org.apache.commons.proxy2.util.Echo;
+import org.apache.commons.proxy2.util.EchoImpl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Properties;
 
-public class TestProxyUtils extends TestCase
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class ProxyUtilsTest extends AbstractTestCase
 {
 //**********************************************************************************************************************
 // Fields
@@ -39,42 +44,47 @@
 // Other Methods
 //**********************************************************************************************************************
 
-    protected void setUp() throws Exception
+    @Before
+    public void setUp() throws Exception
     {
         prevProperties = System.getProperties();
         System.setProperties(new Properties());
     }
 
-    protected void tearDown() throws Exception
+    @After
+    public void tearDown() throws Exception
     {
         System.setProperties(prevProperties);
     }
 
-    public void testCreateNullObject() throws Exception
+    @Test
+    public void testNullValue()
     {
-        final Echo nullEcho = ( Echo ) ProxyUtils
-                .createNullObject(new JavassistProxyFactory(), new Class[] {Echo.class});
-        assertNull(nullEcho.echoBack("hello"));
-        assertNull(nullEcho.echoBack("hello", "world"));
-        assertEquals(( int ) 0, nullEcho.echoBack(12345));
+        assertNullValue(null, String.class);
+        assertNullValue(( char ) 0, Character.TYPE);
+        assertNullValue(0, Integer.TYPE);
+        assertNullValue(( long ) 0, Long.TYPE);
+        assertNullValue(( short ) 0, Short.TYPE);
+        assertNullValue(( double ) 0, Double.TYPE);
+        assertNullValue(( float ) 0, Float.TYPE);
+        assertNullValue(false, Boolean.TYPE);
+        assertNullValue(( byte ) 0, Byte.TYPE);
     }
 
-    public void testCreateNullObjectWithClassLoader() throws Exception
+    private void assertNullValue( Object expected, Class<?> type )
     {
-        final Echo nullEcho = ( Echo ) ProxyUtils.createNullObject(new JavassistProxyFactory(),
-                Echo.class.getClassLoader(),
-                new Class[] {Echo.class});
-        assertNull(nullEcho.echoBack("hello"));
-        assertNull(nullEcho.echoBack("hello", "world"));
-        assertEquals(( int ) 0, nullEcho.echoBack(12345));
+        assertEquals(expected, ProxyUtils.nullValue(type));
     }
 
+    @Test
     public void testGetAllInterfaces()
     {
         assertNull(ProxyUtils.getAllInterfaces(null));
-        assertEquals(Arrays.asList(new Class[] {DuplicateEcho.class, Serializable.class, Echo.class}), Arrays.asList(ProxyUtils.getAllInterfaces(EchoImpl.class)));
+        assertEquals(Arrays.asList(new Class[] {DuplicateEcho.class, Serializable.class, Echo.class}),
+                     Arrays.asList(ProxyUtils.getAllInterfaces(EchoImpl.class)));
     }
 
+    @Test
     public void testGetJavaClassName() throws Exception
     {
         assertEquals("java.lang.Object[]", ProxyUtils.getJavaClassName(Object[].class));
diff --git a/src/test/java/org/apache/commons/proxy/exception/AbstractExceptionClassTestCase.java b/core/src/test/java/org/apache/commons/proxy2/exception/AbstractExceptionClassTestCase.java
similarity index 88%
rename from src/test/java/org/apache/commons/proxy/exception/AbstractExceptionClassTestCase.java
rename to core/src/test/java/org/apache/commons/proxy2/exception/AbstractExceptionClassTestCase.java
index 343ddd6..f065412 100644
--- a/src/test/java/org/apache/commons/proxy/exception/AbstractExceptionClassTestCase.java
+++ b/core/src/test/java/org/apache/commons/proxy2/exception/AbstractExceptionClassTestCase.java
@@ -15,27 +15,29 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.exception;
+package org.apache.commons.proxy2.exception;
 
-import junit.framework.TestCase;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.junit.Test;
+import static org.junit.Assert.*;
 
 /**
  * @author James Carman
  * @since 1.0
  */
-public abstract class AbstractExceptionClassTestCase extends TestCase
+public abstract class AbstractExceptionClassTestCase extends AbstractTestCase
 {
 //**********************************************************************************************************************
 // Fields
 //**********************************************************************************************************************
 
-    private final Class exceptionClass;
+    private final Class<?> exceptionClass;
 
 //**********************************************************************************************************************
 // Constructors
 //**********************************************************************************************************************
 
-    public AbstractExceptionClassTestCase( Class exceptionClass )
+    public AbstractExceptionClassTestCase( Class<?> exceptionClass )
     {
         this.exceptionClass = exceptionClass;
     }
@@ -44,6 +46,7 @@
 // Other Methods
 //**********************************************************************************************************************
 
+    @Test
     public void testCauseOnlyConstructor() throws Exception
     {
         final Exception cause = new Exception();
@@ -52,6 +55,7 @@
         assertEquals(cause, e.getCause());
     }
 
+    @Test
     public void testMessageAndCauseConstructor() throws Exception
     {
         final Exception cause = new Exception();
@@ -61,6 +65,7 @@
         assertEquals(cause, e.getCause());
     }
 
+    @Test
     public void testMessageOnlyConstructor() throws Exception
     {
         final String message = "message";
@@ -69,6 +74,7 @@
         assertNull(e.getCause());
     }
 
+    @Test
     public void testNoArgConstructor() throws Exception
     {
         Exception e = ( Exception ) exceptionClass.getConstructor(new Class[] {}).newInstance(new Object[] {});
diff --git a/src/test/java/org/apache/commons/proxy/exception/TestDelegateProviderException.java b/core/src/test/java/org/apache/commons/proxy2/exception/DelegateProviderExceptionTest.java
similarity index 87%
rename from src/test/java/org/apache/commons/proxy/exception/TestDelegateProviderException.java
rename to core/src/test/java/org/apache/commons/proxy2/exception/DelegateProviderExceptionTest.java
index 45af58c..d1c41ac 100644
--- a/src/test/java/org/apache/commons/proxy/exception/TestDelegateProviderException.java
+++ b/core/src/test/java/org/apache/commons/proxy2/exception/DelegateProviderExceptionTest.java
@@ -15,15 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.exception;
+package org.apache.commons.proxy2.exception;
 
-public class TestDelegateProviderException extends AbstractExceptionClassTestCase
+public class DelegateProviderExceptionTest extends AbstractExceptionClassTestCase
 {
 //**********************************************************************************************************************
 // Constructors
 //**********************************************************************************************************************
 
-    public TestDelegateProviderException()
+    public DelegateProviderExceptionTest()
     {
         super(ObjectProviderException.class);
     }
diff --git a/src/test/java/org/apache/commons/proxy/exception/TestInvocationHandlerException.java b/core/src/test/java/org/apache/commons/proxy2/exception/InvocationHandlerExceptionTest.java
similarity index 87%
rename from src/test/java/org/apache/commons/proxy/exception/TestInvocationHandlerException.java
rename to core/src/test/java/org/apache/commons/proxy2/exception/InvocationHandlerExceptionTest.java
index 13ed169..1534b29 100644
--- a/src/test/java/org/apache/commons/proxy/exception/TestInvocationHandlerException.java
+++ b/core/src/test/java/org/apache/commons/proxy2/exception/InvocationHandlerExceptionTest.java
@@ -15,15 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.exception;
+package org.apache.commons.proxy2.exception;
 
-public class TestInvocationHandlerException extends AbstractExceptionClassTestCase
+public class InvocationHandlerExceptionTest extends AbstractExceptionClassTestCase
 {
 //**********************************************************************************************************************
 // Constructors
 //**********************************************************************************************************************
 
-    public TestInvocationHandlerException()
+    public InvocationHandlerExceptionTest()
     {
         super(InvokerException.class);
     }
diff --git a/src/test/java/org/apache/commons/proxy/exception/TestProxyFactoryException.java b/core/src/test/java/org/apache/commons/proxy2/exception/ProxyFactoryExceptionTest.java
similarity index 88%
rename from src/test/java/org/apache/commons/proxy/exception/TestProxyFactoryException.java
rename to core/src/test/java/org/apache/commons/proxy2/exception/ProxyFactoryExceptionTest.java
index 4498a29..2f56286 100644
--- a/src/test/java/org/apache/commons/proxy/exception/TestProxyFactoryException.java
+++ b/core/src/test/java/org/apache/commons/proxy2/exception/ProxyFactoryExceptionTest.java
@@ -15,19 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.exception;
+package org.apache.commons.proxy2.exception;
 
 /**
  * @author James Carman
  * @since 1.0
  */
-public class TestProxyFactoryException extends AbstractExceptionClassTestCase
+public class ProxyFactoryExceptionTest extends AbstractExceptionClassTestCase
 {
 //**********************************************************************************************************************
 // Constructors
 //**********************************************************************************************************************
 
-    public TestProxyFactoryException()
+    public ProxyFactoryExceptionTest()
     {
         super(ProxyFactoryException.class);
     }
diff --git a/core/src/test/java/org/apache/commons/proxy2/impl/MethodSignatureTest.java b/core/src/test/java/org/apache/commons/proxy2/impl/MethodSignatureTest.java
new file mode 100644
index 0000000..a4471ee
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/impl/MethodSignatureTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.impl;
+
+import java.lang.reflect.Method;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.proxy2.util.AbstractEcho;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.DuplicateEcho;
+import org.apache.commons.proxy2.util.Echo;
+import org.apache.commons.proxy2.util.EchoImpl;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class MethodSignatureTest extends AbstractTestCase
+{
+//**********************************************************************************************************************
+// Other Methods
+//**********************************************************************************************************************
+
+    @Test
+    public void testEquals() throws Exception
+    {
+        final MethodSignature sig = new MethodSignature(Echo.class.getMethod("echoBack", String.class));
+        assertTrue(sig.equals(sig));
+        assertFalse(sig.equals("echoBack"));
+        assertEquals(sig, new MethodSignature(Echo.class.getMethod("echoBack", String.class)));
+        assertEquals(sig, new MethodSignature(DuplicateEcho.class.getMethod("echoBack", String.class)));
+        assertFalse(sig.equals(new MethodSignature(Echo.class.getMethod("echoBack", String.class, String.class))));
+        assertFalse(sig.equals(new MethodSignature(Echo.class.getMethod("echo"))));
+    }
+
+    @Test
+    public void testSerialization() throws Exception
+    {
+        final MethodSignature sig = new MethodSignature(Echo.class.getMethod("echoBack", String.class));
+        assertEquals(sig, SerializationUtils.clone(sig));
+    }
+
+    @Test
+    public void testToString() throws Exception
+    {
+        assertEquals("echo()", new MethodSignature(Echo.class.getMethod("echo")).toString());
+        assertEquals("echoBack(Ljava/lang/String;)", new MethodSignature(Echo.class.getMethod("echoBack", String.class)).toString());
+        assertEquals("echoBack([Ljava/lang/String;)", new MethodSignature(Echo.class.getMethod("echoBack", String[].class)).toString());
+        assertEquals("echoBack([[Ljava/lang/String;)", new MethodSignature(Echo.class.getMethod("echoBack", String[][].class)).toString());
+        assertEquals("echoBack([[[Ljava/lang/String;)", new MethodSignature(Echo.class.getMethod("echoBack", String[][][].class)).toString());
+        assertEquals("echoBack(I)", new MethodSignature(Echo.class.getMethod("echoBack", int.class)).toString());
+        assertEquals("echoBack(Z)", new MethodSignature(Echo.class.getMethod("echoBack", boolean.class)).toString());
+        assertEquals("echoBack(Ljava/lang/String;Ljava/lang/String;)", new MethodSignature(Echo.class.getMethod("echoBack", String.class, String.class)).toString());
+        assertEquals("illegalArgument()", new MethodSignature(Echo.class.getMethod("illegalArgument")).toString());
+        assertEquals("ioException()", new MethodSignature(Echo.class.getMethod("ioException")).toString());
+    }
+
+    @Test
+    public void testToMethod() throws Exception
+    {
+        final MethodSignature sig = new MethodSignature(Echo.class.getMethod("echoBack", String.class));
+
+        assertMethodIs(sig.toMethod(Echo.class), Echo.class, "echoBack", String.class);
+        assertMethodIs(sig.toMethod(AbstractEcho.class), AbstractEcho.class, "echoBack", String.class);
+        assertMethodIs(sig.toMethod(EchoImpl.class), AbstractEcho.class, "echoBack", String.class);
+        assertMethodIs(sig.toMethod(DuplicateEcho.class), DuplicateEcho.class, "echoBack", String.class);
+    }
+
+    private void assertMethodIs(Method method, Class<?> declaredBy, String name, Class<?>... parameterTypes)
+    {
+        assertEquals(declaredBy, method.getDeclaringClass());
+        assertEquals(name, method.getName());
+        assertArrayEquals(parameterTypes, method.getParameterTypes());
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/InterceptorUtilsTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/InterceptorUtilsTest.java
new file mode 100644
index 0000000..fda9dfb
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/InterceptorUtilsTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.Echo;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class InterceptorUtilsTest extends AbstractTestCase
+{
+    @Test
+    public void testConstant() throws Throwable
+    {
+        Interceptor interceptor = InterceptorUtils.constant("Hello!");
+        Invocation invocation = mockInvocation(Echo.class, "echoBack", String.class).withArguments("World!").build();
+        assertEquals("Hello!", interceptor.intercept(invocation));
+    }
+
+    @Test
+    public void testProvider() throws Throwable
+    {
+        Interceptor interceptor = InterceptorUtils.provider(ObjectProviderUtils.constant("Foo!"));
+        Invocation invocation = mockInvocation(Echo.class, "echoBack", String.class).withArguments("World!").build();
+        assertEquals("Foo!", interceptor.intercept(invocation));
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testThrowingExceptionObject() throws Throwable
+    {
+        Interceptor interceptor = InterceptorUtils.throwing(new RuntimeException("Oops!"));
+        Invocation invocation = mockInvocation(Echo.class, "echoBack", String.class).withArguments("World!").build();
+        interceptor.intercept(invocation);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testThrowingProvidedException() throws Throwable
+    {
+        Interceptor interceptor = InterceptorUtils.throwing(ObjectProviderUtils.constant(new RuntimeException("Oops!")));
+        Invocation invocation = mockInvocation(Echo.class, "echoBack", String.class).withArguments("World!").build();
+        interceptor.intercept(invocation);
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/InvokerInterceptorTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/InvokerInterceptorTest.java
new file mode 100644
index 0000000..6fea1f0
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/InvokerInterceptorTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Method;
+
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.Echo;
+import org.junit.Test;
+
+public class InvokerInterceptorTest extends AbstractTestCase
+{
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Other Methods
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void testIntercept() throws Throwable
+    {
+        final InvokerInterceptor interceptor = new InvokerInterceptor(new Invoker()
+        {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable
+            {
+                return "Hello!";
+            }
+        });
+        final Invocation invocation = new MockInvocationBuilder(Echo.class.getDeclaredMethod("echoBack", String.class))
+                .withArguments("foo").build();
+
+        assertEquals("Hello!", interceptor.intercept(invocation));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testWithNullInvoker()
+    {
+        new InvokerInterceptor(null);
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/ObjectProviderInterceptorTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/ObjectProviderInterceptorTest.java
new file mode 100644
index 0000000..09de5e7
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/ObjectProviderInterceptorTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ObjectProviderInterceptorTest extends AbstractTestCase
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void testIntercept() throws Throwable
+    {
+        ObjectProviderInterceptor interceptor = new ObjectProviderInterceptor(ObjectProviderUtils.constant("Hello!"));
+        assertEquals("Hello!", interceptor.intercept(null));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testWithNullProvider()
+    {
+        new ObjectProviderInterceptor(null);
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/SwitchInterceptorTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/SwitchInterceptorTest.java
new file mode 100644
index 0000000..71c311a
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/SwitchInterceptorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor;
+
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.interceptor.matcher.invocation.MethodNameMatcher;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.Echo;
+import org.apache.commons.proxy2.util.MockInvocation;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Method;
+
+import static org.apache.commons.proxy2.interceptor.InterceptorUtils.constant;
+
+public class SwitchInterceptorTest extends AbstractTestCase
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void testWithMultipleAdvices() throws Throwable
+    {
+        SwitchInterceptor interceptor = new SwitchInterceptor();
+        interceptor.when(new MethodNameMatcher("echo")).then(constant("bar"));
+        interceptor.when(new MethodNameMatcher("echoBack")).then(constant("baz"));
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo", "foo");
+        assertEquals("baz", interceptor.intercept(invocation));
+    }
+
+    @Test
+    public void testWithNoAdvice() throws Throwable
+    {
+        SwitchInterceptor interceptor = new SwitchInterceptor();
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo", "foo");
+        assertEquals("foo", interceptor.intercept(invocation));
+    }
+
+    @Test
+    public void testWithSingleAdviceWhichDoesNotMatch() throws Throwable
+    {
+        SwitchInterceptor interceptor = new SwitchInterceptor().when(new MethodNameMatcher("echoBackZZZZ")).then(constant("bar"));
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo", "foo");
+        assertEquals("foo", interceptor.intercept(invocation));
+    }
+
+    @Test
+    public void testWithSingleAdviceWhichMatches() throws Throwable
+    {
+        SwitchInterceptor interceptor = new SwitchInterceptor().when(new MethodNameMatcher("echoBack")).then(constant("bar"));
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo", "foo");
+        assertEquals("bar", interceptor.intercept(invocation));
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/DeclaredByMatcherTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/DeclaredByMatcherTest.java
new file mode 100644
index 0000000..7f1853b
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/DeclaredByMatcherTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.interceptor.matcher.invocation.DeclaredByMatcher;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.Echo;
+import org.apache.commons.proxy2.util.EchoImpl;
+import org.apache.commons.proxy2.util.MockInvocation;
+import org.junit.Test;
+
+public class DeclaredByMatcherTest extends AbstractTestCase
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void testExactMatchNonMatching() throws Throwable
+    {
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo");
+        InvocationMatcher matcher = new DeclaredByMatcher(EchoImpl.class, true);
+        assertFalse(matcher.matches(invocation));
+    }
+
+    @Test
+    public void testWithSupertypeMatch() throws Throwable
+    {
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo");
+        InvocationMatcher matcher = new DeclaredByMatcher(EchoImpl.class);
+        assertTrue(matcher.matches(invocation));
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/MethodNameMatcherTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/MethodNameMatcherTest.java
new file mode 100644
index 0000000..e23ce0a
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/MethodNameMatcherTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher;
+
+import org.apache.commons.proxy2.interceptor.matcher.invocation.MethodNameMatcher;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.Echo;
+import org.apache.commons.proxy2.util.MockInvocation;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+
+import static org.junit.Assert.*;
+
+public class MethodNameMatcherTest extends AbstractTestCase
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void testWithMatchingMethod() throws Exception
+    {
+        MethodNameMatcher matcher = new MethodNameMatcher("echo");
+        final Method method = Echo.class.getMethod("echo");
+        assertTrue(matcher.matches(new MockInvocation(method,null)));
+    }
+
+    @Test
+    public void testWithNonMatchingMethod() throws Exception
+    {
+        MethodNameMatcher matcher = new MethodNameMatcher("foo");
+        final Method method = Echo.class.getMethod("echo");
+        assertFalse(matcher.matches(new MockInvocation(method, null)));
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/ReturnTypeMatcherTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/ReturnTypeMatcherTest.java
new file mode 100644
index 0000000..8c50ba5
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/ReturnTypeMatcherTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher;
+
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.interceptor.matcher.invocation.ReturnTypeMatcher;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.Echo;
+import org.apache.commons.proxy2.util.MockInvocation;
+import org.junit.Test;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import static org.junit.Assert.*;
+
+public class ReturnTypeMatcherTest extends AbstractTestCase
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void testExactMatchNonMatching() throws Throwable
+    {
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo");
+        InvocationMatcher matcher = new ReturnTypeMatcher(Serializable.class, true);
+        assertFalse(matcher.matches(invocation));
+    }
+
+    @Test
+    public void testMatchVoid() throws Throwable
+    {
+        Method method = Echo.class.getMethod("echo");
+        Invocation invocation = new MockInvocation(method, null);
+        InvocationMatcher matcher = new ReturnTypeMatcher(Void.TYPE);
+        assertTrue(matcher.matches(invocation));
+    }
+
+    @Test
+    public void testWithSupertypeMatch() throws Throwable
+    {
+        Method method = Echo.class.getMethod("echoBack", String.class);
+        Invocation invocation = new MockInvocation(method, "foo");
+        InvocationMatcher matcher = new ReturnTypeMatcher(Serializable.class);
+        assertTrue(matcher.matches(invocation));
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/argument/ArgumentMatcherUtilsTest.java b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/argument/ArgumentMatcherUtilsTest.java
new file mode 100644
index 0000000..b261b3a
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/interceptor/matcher/argument/ArgumentMatcherUtilsTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.interceptor.matcher.argument;
+
+import org.apache.commons.proxy2.interceptor.matcher.ArgumentMatcher;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ArgumentMatcherUtilsTest
+{
+    @Test
+    public void testAny() throws Exception
+    {
+        ArgumentMatcher<Object> matcher =ArgumentMatcherUtils.any();
+        assertTrue(matcher.matches(null));
+        assertTrue(matcher.matches("Hello!"));
+        assertTrue(matcher.matches(12345));
+    }
+
+    @Test
+    public void testEq() throws Exception
+    {
+        ArgumentMatcher<String> matcher = ArgumentMatcherUtils.eq("Hello");
+        assertTrue(matcher.matches("Hello"));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testGt() throws Exception
+    {
+        ArgumentMatcher<Integer> matcher = ArgumentMatcherUtils.gt(5);
+        assertTrue(matcher.matches(6));
+        assertFalse(matcher.matches(5));
+        assertFalse(matcher.matches(1));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testGte() throws Exception
+    {
+        ArgumentMatcher<Integer> matcher = ArgumentMatcherUtils.gte(5);
+        assertTrue(matcher.matches(6));
+        assertTrue(matcher.matches(5));
+        assertFalse(matcher.matches(1));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testIsA() throws Exception
+    {
+        ArgumentMatcher<Object> matcher = ArgumentMatcherUtils.isA(String.class);
+        assertFalse(matcher.matches(null));
+        assertTrue(matcher.matches("Hello"));
+        assertFalse(matcher.matches(123));
+    }
+
+    @Test
+    public void testIsNull() throws Exception
+    {
+        ArgumentMatcher<Object> matcher = ArgumentMatcherUtils.isNull();
+        assertTrue(matcher.matches(null));
+        assertFalse(matcher.matches("Hello"));
+        assertFalse(matcher.matches(123));
+    }
+
+    @Test
+    public void testLt() throws Exception
+    {
+        ArgumentMatcher<Integer> matcher = ArgumentMatcherUtils.lt(5);
+        assertTrue(matcher.matches(4));
+        assertFalse(matcher.matches(5));
+        assertFalse(matcher.matches(19));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testLte() throws Exception
+    {
+        ArgumentMatcher<Integer> matcher = ArgumentMatcherUtils.lte(5);
+        assertTrue(matcher.matches(4));
+        assertTrue(matcher.matches(5));
+        assertFalse(matcher.matches(19));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testMatches() throws Exception
+    {
+        ArgumentMatcher<String> matcher = ArgumentMatcherUtils.matches("(abc)+");
+        assertTrue(matcher.matches("abc"));
+        assertTrue(matcher.matches("abcabc"));
+        assertFalse(matcher.matches(""));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testNotNull() throws Exception
+    {
+        ArgumentMatcher<String> matcher = ArgumentMatcherUtils.notNull();
+        assertTrue(matcher.matches("Hello"));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testStartsWith() throws Exception
+    {
+        ArgumentMatcher<String> matcher = ArgumentMatcherUtils.startsWith("abc");
+        assertTrue(matcher.matches("abc"));
+        assertTrue(matcher.matches("abcd"));
+        assertFalse(matcher.matches("ab"));
+        assertFalse(matcher.matches(null));
+    }
+
+    @Test
+    public void testEndsWith() throws Exception
+    {
+        ArgumentMatcher<String> matcher = ArgumentMatcherUtils.endsWith("abc");
+        assertTrue(matcher.matches("abc"));
+        assertTrue(matcher.matches("dabc"));
+        assertFalse(matcher.matches("ab"));
+        assertFalse(matcher.matches(null));
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/proxy2/invoker/DelegatingInvokerTest.java b/core/src/test/java/org/apache/commons/proxy2/invoker/DelegatingInvokerTest.java
new file mode 100644
index 0000000..d7c585b
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/invoker/DelegatingInvokerTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.invoker;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+import org.apache.commons.proxy2.util.Echo;
+import org.apache.commons.proxy2.util.EchoImpl;
+import org.junit.Test;
+
+public class DelegatingInvokerTest
+{
+
+    @Test
+    public void test() throws Throwable
+    {
+        final Invoker invoker = new DelegatingInvoker<Echo>(ObjectProviderUtils.constant(new EchoImpl()));
+
+        assertEquals("foo",
+                invoker.invoke(null, Echo.class.getDeclaredMethod("echoBack", String.class), new Object[] { "foo" }));
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/proxy/provider/TestBeanProvider.java b/core/src/test/java/org/apache/commons/proxy2/provider/BeanProviderTest.java
similarity index 64%
rename from src/test/java/org/apache/commons/proxy/provider/TestBeanProvider.java
rename to core/src/test/java/org/apache/commons/proxy2/provider/BeanProviderTest.java
index 8973bfa..415c895 100644
--- a/src/test/java/org/apache/commons/proxy/provider/TestBeanProvider.java
+++ b/core/src/test/java/org/apache/commons/proxy2/provider/BeanProviderTest.java
@@ -15,59 +15,42 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.provider;
+package org.apache.commons.proxy2.provider;
 
-import org.apache.commons.proxy.exception.ObjectProviderException;
-import org.apache.commons.proxy.util.AbstractTestCase;
+import org.apache.commons.proxy2.exception.ObjectProviderException;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.junit.Test;
 
-public class TestBeanProvider extends AbstractTestCase
+public class BeanProviderTest extends AbstractTestCase
 {
 //**********************************************************************************************************************
 // Other Methods
 //**********************************************************************************************************************
 
+    @Test(expected = ObjectProviderException.class)
     public void testAbstractBeanClass()
     {
-        try
-        {
-            final BeanProvider p = new BeanProvider();
-            p.setBeanClass(Number.class);
-            p.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
+        final BeanProvider<Number> p = new BeanProvider<Number>(Number.class);
+        p.getObject();
     }
 
+    @Test(expected = ObjectProviderException.class)
     public void testNonAccessibleConstructor()
     {
-        try
-        {
-            new BeanProvider(MyBean.class).getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
+        new BeanProvider<MyBean>(MyBean.class).getObject();
     }
 
+    @Test
     public void testSerialization()
     {
-        assertSerializable(new BeanProvider(MyBean.class));
+        assertSerializable(new BeanProvider<MyBean>(MyBean.class));
     }
 
+    @Test(expected = NullPointerException.class)
     public void testWithNullBeanClass()
     {
-        try
-        {
-            final BeanProvider p = new BeanProvider();
-            p.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
+        final BeanProvider<Object> p = new BeanProvider<Object>(null);
+        p.getObject();
     }
 
 //**********************************************************************************************************************
diff --git a/src/test/java/org/apache/commons/proxy/provider/TestCloningProvider.java b/core/src/test/java/org/apache/commons/proxy2/provider/CloningProviderTest.java
similarity index 65%
rename from src/test/java/org/apache/commons/proxy/provider/TestCloningProvider.java
rename to core/src/test/java/org/apache/commons/proxy2/provider/CloningProviderTest.java
index adac321..c332f49 100644
--- a/src/test/java/org/apache/commons/proxy/provider/TestCloningProvider.java
+++ b/core/src/test/java/org/apache/commons/proxy2/provider/CloningProviderTest.java
@@ -15,74 +15,67 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.provider;
+package org.apache.commons.proxy2.provider;
 
-import org.apache.commons.proxy.exception.ObjectProviderException;
-import org.apache.commons.proxy.util.AbstractTestCase;
+import org.apache.commons.proxy2.exception.ObjectProviderException;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.junit.Test;
 
 import java.util.Date;
 
-public class TestCloningProvider extends AbstractTestCase
+import static org.junit.Assert.*;
+
+public class CloningProviderTest extends AbstractTestCase
 {
 //**********************************************************************************************************************
 // Other Methods
 //**********************************************************************************************************************
 
+    @Test
     public void testSerialization()
     {
-        assertSerializable(new CloningProvider(new Date()));
+        assertSerializable(new CloningProvider<Date>(new Date()));
     }
 
+    @Test
     public void testValidCloneable()
     {
         final Date now = new Date();
-        final CloningProvider provider = new CloningProvider(now);
-        final Date clone1 = ( Date ) provider.getObject();
+        final CloningProvider<Date> provider = new CloningProvider<Date>(now);
+        final Date clone1 = (Date) provider.getObject();
         assertEquals(now, clone1);
         assertNotSame(now, clone1);
-        final Date clone2 = ( Date ) provider.getObject();
+        final Date clone2 = (Date) provider.getObject();
         assertEquals(now, clone2);
         assertNotSame(now, clone2);
         assertNotSame(clone2, clone1);
     }
 
+    @Test
     public void testWithExceptionThrown()
     {
-        final CloningProvider provider = new CloningProvider(new ExceptionCloneable());
+        final CloningProvider<ExceptionCloneable> provider = new CloningProvider<ExceptionCloneable>(new ExceptionCloneable());
         try
         {
             provider.getObject();
             fail();
         }
-        catch( ObjectProviderException e )
+        catch (ObjectProviderException e)
         {
         }
     }
 
+    @Test(expected = IllegalArgumentException.class)
     public void testWithInvalidCloneable()
     {
-        final CloningProvider provider = new CloningProvider(new InvalidCloneable());
-        try
-        {
-            provider.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
+        new CloningProvider<InvalidCloneable>(new InvalidCloneable());
     }
 
-    public void testWithPrivateCloneMethod()
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithProtectedCloneMethod()
     {
-        final CloningProvider provider = new CloningProvider(new PrivateCloneable());
-        try
-        {
-            provider.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
+        final CloningProvider<ProtectedCloneable> provider = new CloningProvider<ProtectedCloneable>(new ProtectedCloneable());
+        provider.getObject();
     }
 
 //**********************************************************************************************************************
@@ -101,7 +94,7 @@
     {
     }
 
-    public static class PrivateCloneable implements Cloneable
+    public static class ProtectedCloneable implements Cloneable
     {
         protected Object clone()
         {
diff --git a/src/test/java/org/apache/commons/proxy/provider/TestConstantProvider.java b/core/src/test/java/org/apache/commons/proxy2/provider/ConstantProviderTest.java
similarity index 75%
rename from src/test/java/org/apache/commons/proxy/provider/TestConstantProvider.java
rename to core/src/test/java/org/apache/commons/proxy2/provider/ConstantProviderTest.java
index 8c4eee3..6c756e7 100644
--- a/src/test/java/org/apache/commons/proxy/provider/TestConstantProvider.java
+++ b/core/src/test/java/org/apache/commons/proxy2/provider/ConstantProviderTest.java
@@ -15,28 +15,33 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.provider;
+package org.apache.commons.proxy2.provider;
 
-import org.apache.commons.proxy.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
 
 /**
  * @since 1.0
  */
-public class TestConstantProvider extends AbstractTestCase
+public class ConstantProviderTest extends AbstractTestCase
 {
 //**********************************************************************************************************************
 // Other Methods
 //**********************************************************************************************************************
 
+    @Test
     public void testGetObject() throws Exception
     {
         final String s = "Hello, World!";
-        final ConstantProvider provider = new ConstantProvider(s);
+        final ConstantProvider<String> provider = new ConstantProvider<String>(s);
         assertSame(s, provider.getObject());
     }
 
+    @Test
     public void testSerialization()
     {
-        assertSerializable(new ConstantProvider("Hello, World!"));
+        assertSerializable(new ConstantProvider<String>("Hello, World!"));
     }
 }
diff --git a/src/test/java/org/apache/commons/proxy/provider/CountingProvider.java b/core/src/test/java/org/apache/commons/proxy2/provider/CountingProvider.java
similarity index 88%
rename from src/test/java/org/apache/commons/proxy/provider/CountingProvider.java
rename to core/src/test/java/org/apache/commons/proxy2/provider/CountingProvider.java
index ae4d3e6..18d46a2 100644
--- a/src/test/java/org/apache/commons/proxy/provider/CountingProvider.java
+++ b/core/src/test/java/org/apache/commons/proxy2/provider/CountingProvider.java
@@ -15,15 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.provider;
+package org.apache.commons.proxy2.provider;
 
-import org.apache.commons.proxy.ObjectProvider;
+import org.apache.commons.proxy2.ObjectProvider;
 
 /**
  * @author James Carman
  * @since 1.0
  */
-public class CountingProvider extends ProviderDecorator
+public class CountingProvider<T> extends ProviderDecorator<T>
 {
 //**********************************************************************************************************************
 // Fields
@@ -35,7 +35,7 @@
 // Constructors
 //**********************************************************************************************************************
 
-    public CountingProvider( ObjectProvider inner )
+    public CountingProvider( ObjectProvider<? extends T> inner )
     {
         super(inner);
     }
@@ -45,7 +45,7 @@
 //**********************************************************************************************************************
 
 
-    public synchronized Object getObject()
+    public synchronized T getObject()
     {
         count++;
         return super.getObject();
diff --git a/src/test/java/org/apache/commons/proxy/provider/TestNullProvider.java b/core/src/test/java/org/apache/commons/proxy2/provider/NullProviderTest.java
similarity index 76%
rename from src/test/java/org/apache/commons/proxy/provider/TestNullProvider.java
rename to core/src/test/java/org/apache/commons/proxy2/provider/NullProviderTest.java
index 12f59f5..fbf8b04 100644
--- a/src/test/java/org/apache/commons/proxy/provider/TestNullProvider.java
+++ b/core/src/test/java/org/apache/commons/proxy2/provider/NullProviderTest.java
@@ -15,28 +15,33 @@
  * limitations under the License.
  */
 
-package org.apache.commons.proxy.provider;
+package org.apache.commons.proxy2.provider;
 
-import org.apache.commons.proxy.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
 
 /**
  * @author James Carman
  * @since 1.0
  */
-public class TestNullProvider extends AbstractTestCase
+public class NullProviderTest extends AbstractTestCase
 {
 //**********************************************************************************************************************
 // Other Methods
 //**********************************************************************************************************************
 
+    @Test
     public void testGetObject()
     {
-        final NullProvider provider = new NullProvider();
+        final NullProvider<Object> provider = new NullProvider<Object>();
         assertNull(provider.getObject());
     }
 
+    @Test
     public void testSerialization()
     {
-        assertSerializable(new NullProvider());
+        assertSerializable(new NullProvider<Object>());
     }
 }
diff --git a/core/src/test/java/org/apache/commons/proxy2/provider/ObjectProviderUtilsTest.java b/core/src/test/java/org/apache/commons/proxy2/provider/ObjectProviderUtilsTest.java
new file mode 100644
index 0000000..89ce98a
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/provider/ObjectProviderUtilsTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.provider;
+
+import org.apache.commons.proxy2.util.AbstractTestCase;
+import org.apache.commons.proxy2.util.EchoImpl;
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.junit.Assert.*;
+
+public class ObjectProviderUtilsTest extends AbstractTestCase
+{
+    @Test
+    public void testBean() throws Exception
+    {
+        assertTrue(ObjectProviderUtils.bean(EchoImpl.class) instanceof BeanProvider);
+    }
+
+    @Test
+    public void testCloning() throws Exception
+    {
+        assertTrue(ObjectProviderUtils.cloning(new Date()) instanceof CloningProvider);
+    }
+
+    @Test
+    public void testConstant() throws Exception
+    {
+        assertTrue(ObjectProviderUtils.constant("Hello") instanceof ConstantProvider);
+    }
+
+    @Test
+    public void testNullValue() throws Exception
+    {
+        assertTrue(ObjectProviderUtils.nullValue() instanceof NullProvider);
+    }
+
+    @Test
+    public void testSingleton() throws Exception
+    {
+        assertTrue(ObjectProviderUtils.singleton(new ConstantProvider<Object>("Hello")) instanceof SingletonProvider);
+    }
+}
diff --git a/src/test/java/org/apache/commons/proxy/util/AbstractEcho.java b/core/src/test/java/org/apache/commons/proxy2/util/AbstractEcho.java
similarity index 94%
rename from src/test/java/org/apache/commons/proxy/util/AbstractEcho.java
rename to core/src/test/java/org/apache/commons/proxy2/util/AbstractEcho.java
index 68459b0..fe8bc51 100644
--- a/src/test/java/org/apache/commons/proxy/util/AbstractEcho.java
+++ b/core/src/test/java/org/apache/commons/proxy2/util/AbstractEcho.java
@@ -1,36 +1,37 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.util;
-
-import java.io.Serializable;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public abstract class AbstractEcho implements Echo, Serializable
-{
-//**********************************************************************************************************************
-// Echo Implementation
-//**********************************************************************************************************************
-
-    public String echoBack( String message )
-    {
-        return message;
-    }
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.util;

+

+import java.io.Serializable;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+@SuppressWarnings("serial")

+public abstract class AbstractEcho implements Echo, Serializable

+{

+//**********************************************************************************************************************

+// Echo Implementation

+//**********************************************************************************************************************

+

+    public String echoBack( String message )

+    {

+        return message;

+    }

+}

diff --git a/core/src/test/java/org/apache/commons/proxy2/util/AbstractTestCase.java b/core/src/test/java/org/apache/commons/proxy2/util/AbstractTestCase.java
new file mode 100644
index 0000000..78fb664
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/util/AbstractTestCase.java
@@ -0,0 +1,71 @@
+package org.apache.commons.proxy2.util;

+

+import org.apache.commons.lang3.SerializationUtils;

+import org.apache.commons.lang3.Validate;

+import org.apache.commons.lang3.builder.Builder;

+import org.apache.commons.proxy2.Invocation;

+import org.apache.commons.proxy2.ProxyUtils;

+

+import java.io.Serializable;

+import java.lang.reflect.Method;

+

+import static org.junit.Assert.*;

+

+/**

+ * @author James Carman

+ * @since 1.1

+ */

+public abstract class AbstractTestCase

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    protected void assertSerializable( Object o )

+    {

+        assertTrue(o instanceof Serializable);

+        SerializationUtils.clone(( Serializable ) o);

+    }

+

+    protected MockInvocationBuilder mockInvocation(Class<?> type, String name, Class<?>... argumentTypes)

+    {

+        try

+        {

+            return new MockInvocationBuilder(Validate.notNull(type).getMethod(name, argumentTypes));

+        }

+        catch (NoSuchMethodException e)

+        {

+            throw new IllegalArgumentException("Method not found.", e);

+        }

+    }

+

+    protected static final class MockInvocationBuilder implements Builder<Invocation>

+    {

+        private final Method method;

+        private Object[] arguments = ProxyUtils.EMPTY_ARGUMENTS;

+        private Object returnValue = null;

+

+        public MockInvocationBuilder(Method method)

+        {

+            this.method = method;

+        }

+

+        public MockInvocationBuilder withArguments(Object... arguments)

+        {

+            this.arguments = arguments;

+            return this;

+        }

+

+        public MockInvocationBuilder returning(Object value)

+        {

+            this.returnValue = value;

+            return this;

+        }

+

+        @Override

+        public Invocation build()

+        {

+            return new MockInvocation(method, returnValue, arguments);

+        }

+    }

+}

diff --git a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java b/core/src/test/java/org/apache/commons/proxy2/util/DuplicateEcho.java
similarity index 96%
rename from src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
rename to core/src/test/java/org/apache/commons/proxy2/util/DuplicateEcho.java
index f73551d..cc64d97 100644
--- a/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
+++ b/core/src/test/java/org/apache/commons/proxy2/util/DuplicateEcho.java
@@ -1,31 +1,31 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.util;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public interface DuplicateEcho
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public String echoBack( String message );
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.util;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+public interface DuplicateEcho

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    public String echoBack( String message );

+}

diff --git a/src/test/java/org/apache/commons/proxy/util/Echo.java b/core/src/test/java/org/apache/commons/proxy2/util/Echo.java
similarity index 90%
rename from src/test/java/org/apache/commons/proxy/util/Echo.java
rename to core/src/test/java/org/apache/commons/proxy2/util/Echo.java
index 549fe29..af04e06 100644
--- a/src/test/java/org/apache/commons/proxy/util/Echo.java
+++ b/core/src/test/java/org/apache/commons/proxy2/util/Echo.java
@@ -1,47 +1,52 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.util;
-
-import java.io.IOException;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public interface Echo
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void echo();
-
-    public String echoBack( String message );
-
-    public String echoBack( String[] messages );
-
-    public int echoBack( int i );
-
-    public boolean echoBack( boolean b );
-
-    public String echoBack( String message1, String message2 );
-
-    public void illegalArgument();
-
-    public void ioException() throws IOException;
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.util;

+

+import java.io.IOException;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+public interface Echo

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    public void echo();

+

+    public String echoBack( String message );

+

+    public String echoBack( String[] messages );

+

+    public String echoBack( String[][] messages );

+

+    public String echoBack( String[][][] messages );

+

+    public int echoBack( int i );

+

+    public boolean echoBack( boolean b );

+

+    public String echoBack( String message1, String message2 );

+

+    public void illegalArgument();

+

+    public void ioException() throws IOException;

+

+}

diff --git a/src/test/java/org/apache/commons/proxy/util/EchoImpl.java b/core/src/test/java/org/apache/commons/proxy2/util/EchoImpl.java
similarity index 78%
rename from src/test/java/org/apache/commons/proxy/util/EchoImpl.java
rename to core/src/test/java/org/apache/commons/proxy2/util/EchoImpl.java
index 10cd019..66169d5 100644
--- a/src/test/java/org/apache/commons/proxy/util/EchoImpl.java
+++ b/core/src/test/java/org/apache/commons/proxy2/util/EchoImpl.java
@@ -1,79 +1,99 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.util;
-
-import java.io.IOException;
-import java.io.Serializable;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class EchoImpl extends AbstractEcho implements DuplicateEcho, Serializable
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static final long serialVersionUID = -4844873352607521103L;
-
-//**********************************************************************************************************************
-// Echo Implementation
-//**********************************************************************************************************************
-
-
-    public void echo()
-    {
-    }
-
-    public boolean echoBack( boolean b )
-    {
-        return b;
-    }
-
-    public String echoBack( String[] messages )
-    {
-        final StringBuffer sb = new StringBuffer();
-        for( int i = 0; i < messages.length; i++ )
-        {
-            String message = messages[i];
-            sb.append(message);
-        }
-        return sb.toString();
-    }
-
-    public int echoBack( int i )
-    {
-        return i;
-    }
-
-    public String echoBack( String message1, String message2 )
-    {
-        return message1 + message2;
-    }
-
-    public void illegalArgument()
-    {
-        throw new IllegalArgumentException("dummy message");
-    }
-
-    public void ioException() throws IOException
-    {
-        throw new IOException("dummy message");
-    }
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.util;

+

+import java.io.IOException;

+import java.io.Serializable;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+public class EchoImpl extends AbstractEcho implements DuplicateEcho, Serializable

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private static final long serialVersionUID = -4844873352607521103L;

+

+//**********************************************************************************************************************

+// Echo Implementation

+//**********************************************************************************************************************

+

+

+    public void echo()

+    {

+    }

+

+    public boolean echoBack( boolean b )

+    {

+        return b;

+    }

+

+    public String echoBack( String[] messages )

+    {

+        final StringBuilder sb = new StringBuilder();

+        for( int i = 0; i < messages.length; i++ )

+        {

+            String message = messages[i];

+            sb.append(message);

+        }

+        return sb.toString();

+    }

+

+    public String echoBack( String[][] messages )

+    {

+        final StringBuilder sb = new StringBuilder();

+        for( int i = 0; i < messages.length; i++ )

+        {

+            sb.append(echoBack(messages[i]));

+        }

+        return sb.toString();

+    }

+

+    public String echoBack( String[][][] messages )

+    {

+        final StringBuilder sb = new StringBuilder();

+        for( int i = 0; i < messages.length; i++ )

+        {

+            sb.append(echoBack(messages[i]));

+        }

+        return sb.toString();

+    }

+

+    public int echoBack( int i )

+    {

+        return i;

+    }

+

+    public String echoBack( String message1, String message2 )

+    {

+        return message1 + message2;

+    }

+

+    public void illegalArgument()

+    {

+        throw new IllegalArgumentException("dummy message");

+    }

+

+    public void ioException() throws IOException

+    {

+        throw new IOException("dummy message");

+    }

+}

diff --git a/core/src/test/java/org/apache/commons/proxy2/util/MockInvocation.java b/core/src/test/java/org/apache/commons/proxy2/util/MockInvocation.java
new file mode 100644
index 0000000..92c74e5
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy2/util/MockInvocation.java
@@ -0,0 +1,56 @@
+package org.apache.commons.proxy2.util;
+
+import org.apache.commons.proxy2.Invocation;
+
+import java.lang.reflect.Method;
+
+public class MockInvocation implements Invocation
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private final Method method;
+    private final Object[] arguments;
+    private final Object returnValue;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Constructors
+//----------------------------------------------------------------------------------------------------------------------
+
+    public MockInvocation(Method method, Object returnValue, Object... arguments)
+    {
+        this.returnValue = returnValue;
+        this.arguments = arguments;
+        this.method = method;
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Invocation Implementation
+//----------------------------------------------------------------------------------------------------------------------
+
+
+    @Override
+    public Object[] getArguments()
+    {
+        return arguments;
+    }
+
+    @Override
+    public Method getMethod()
+    {
+        return method;
+    }
+
+    @Override
+    public Object getProxy()
+    {
+        return null;
+    }
+
+    @Override
+    public Object proceed() throws Throwable
+    {
+        return returnValue;
+    }
+}
diff --git a/src/test/java/org/apache/commons/proxy/util/QuoteService.java b/core/src/test/java/org/apache/commons/proxy2/util/QuoteService.java
similarity index 96%
rename from src/test/java/org/apache/commons/proxy/util/QuoteService.java
rename to core/src/test/java/org/apache/commons/proxy2/util/QuoteService.java
index 82c37d7..bd8d841 100644
--- a/src/test/java/org/apache/commons/proxy/util/QuoteService.java
+++ b/core/src/test/java/org/apache/commons/proxy2/util/QuoteService.java
@@ -1,34 +1,34 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.util;
-
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public interface QuoteService extends Remote
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public float getQuote( String symbol ) throws RemoteException;
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.util;

+

+import java.rmi.Remote;

+import java.rmi.RemoteException;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+public interface QuoteService extends Remote

+{

+//**********************************************************************************************************************

+// Other Methods

+//**********************************************************************************************************************

+

+    public float getQuote( String symbol ) throws RemoteException;

+}

diff --git a/src/test/java/org/apache/commons/proxy/util/SuffixInterceptor.java b/core/src/test/java/org/apache/commons/proxy2/util/SuffixInterceptor.java
similarity index 85%
rename from src/test/java/org/apache/commons/proxy/util/SuffixInterceptor.java
rename to core/src/test/java/org/apache/commons/proxy2/util/SuffixInterceptor.java
index c98a6de..466925e 100644
--- a/src/test/java/org/apache/commons/proxy/util/SuffixInterceptor.java
+++ b/core/src/test/java/org/apache/commons/proxy2/util/SuffixInterceptor.java
@@ -1,53 +1,59 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.util;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class SuffixInterceptor implements Interceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final String suffix;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public SuffixInterceptor( String suffix )
-    {
-        this.suffix = suffix;
-    }
-
-//**********************************************************************************************************************
-// Interceptor Implementation
-//**********************************************************************************************************************
-
-
-    public Object intercept( Invocation methodInvocation ) throws Throwable
-    {
-        return methodInvocation.proceed() + suffix;
-    }
-}
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.util;

+

+import org.apache.commons.proxy2.Interceptor;

+import org.apache.commons.proxy2.Invocation;

+

+/**

+ * @author James Carman

+ * @since 1.0

+ */

+@SuppressWarnings("serial")

+public class SuffixInterceptor implements Interceptor

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private final String suffix;

+

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+

+    public SuffixInterceptor( String suffix )

+    {

+        this.suffix = suffix;

+    }

+

+//**********************************************************************************************************************

+// Interceptor Implementation

+//**********************************************************************************************************************

+

+

+    public Object intercept( Invocation methodInvocation ) throws Throwable

+    {

+    	Object result = methodInvocation.proceed();

+    	if (result instanceof String)

+    	{

+    		result = ((String) result) + suffix;

+    	}

+    	return result;

+    }

+}

diff --git a/src/test/resources/log4j.properties b/core/src/test/resources/log4j.properties
similarity index 99%
rename from src/test/resources/log4j.properties
rename to core/src/test/resources/log4j.properties
index b981287..1a98c66 100644
--- a/src/test/resources/log4j.properties
+++ b/core/src/test/resources/log4j.properties
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+
 log4j.rootLogger=DEBUG, console
 log4j.appender.console=org.apache.log4j.ConsoleAppender
 log4j.appender.console.layout=org.apache.log4j.PatternLayout
diff --git a/doap_proxy.rdf b/doap_proxy.rdf
deleted file mode 100644
index 095c057..0000000
--- a/doap_proxy.rdf
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0"?>
-<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:asfext="http://projects.apache.org/ns/asfext#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:doap="http://usefulinc.com/ns/doap#" xml:lang="en">
-  <Project rdf:about="http://commons.apache.org/proxy/">
-    <name>Apache Commons Proxy</name>
-    <homepage rdf:resource="http://commons.apache.org/proxy/"/>
-    <programming-language>Java</programming-language>
-    <category rdf:resource="http://projects.apache.org/category/library"/>
-    <license rdf:resource="http://usefulinc.com/doap/licenses/asl20"/>
-    <bug-database rdf:resource="http://issues.apache.org/jira/browse/POOL"/>
-    <download-page rdf:resource="http://commons.apache.org/proxy/download_proxy.cgi"/>
-    <asfext:pmc rdf:resource="http://commons.apache.org/"/>
-    <shortdesc xml:lang="en">Commons Dynamic Proxy Library</shortdesc>
-    <description xml:lang="en">Commons Dynamic Proxy Library</description>
-    <repository>
-      <SVNRepository>
-        <browse rdf:resource="http://svn.apache.org/repos/asf/commons/proper/proxy/trunk"/>
-        <location rdf:resource="http://svn.apache.org/repos/asf/commons/proper/proxy"/>
-      </SVNRepository>
-    </repository>
-    <release>
-      <Version>
-        <name>commons-proxy</name>
-        <created>2008-02-28</created>
-        <revision>1.0</revision>
-      </Version>
-    </release>
-    <mailing-list rdf:resource="http://commons.apache.org/mail-lists.html"/>
-  </Project>
-</rdf:RDF>
diff --git a/javassist/pom.xml b/javassist/pom.xml
new file mode 100644
index 0000000..3782009
--- /dev/null
+++ b/javassist/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>commons-proxy2-parent</artifactId>
+        <groupId>org.apache.commons</groupId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>commons-proxy2-javassist</artifactId>
+    <name>Commons Proxy Javassist Proxies Module</name>
+    <description>Proxies based on classes dynamically generated using Javassist
+    </description>
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>jboss</groupId>
+            <artifactId>javassist</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/commons/proxy/factory/javassist/JavassistInvocation.java b/javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistInvocation.java
similarity index 74%
rename from src/main/java/org/apache/commons/proxy/factory/javassist/JavassistInvocation.java
rename to javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistInvocation.java
index 5a2e8ae..0bf9a30 100644
--- a/src/main/java/org/apache/commons/proxy/factory/javassist/JavassistInvocation.java
+++ b/javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistInvocation.java
@@ -1,223 +1,237 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.javassist;
-
-import javassist.CannotCompileException;
-import javassist.CtClass;
-import javassist.CtConstructor;
-import javassist.CtMethod;
-import org.apache.commons.proxy.Invocation;
-import org.apache.commons.proxy.ProxyUtils;
-
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-/**
- * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link Invocation} implementation.  This
- * class actually serves as the superclass for all <a href="http://www.jboss.org/products/javassist">Javassist</a>-based
- * method invocations.  Subclasses are dynamically created to deal with specific interface methods (they're hard-wired).
- *
- * @author James Carman
- * @since 1.0
- */
-public abstract class JavassistInvocation implements Invocation
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static WeakHashMap loaderToClassCache = new WeakHashMap();
-    protected final Method method;
-    protected final Object target;
-    protected final Object[] arguments;
-
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    private static String createCastExpression( Class type, String objectToCast )
-    {
-        if( !type.isPrimitive() )
-        {
-            return "( " + ProxyUtils.getJavaClassName(type) + " )" + objectToCast;
-        }
-        else
-        {
-            return "( ( " + ProxyUtils.getWrapperClass(type).getName() + " )" + objectToCast + " )." +
-                    type.getName() + "Value()";
-        }
-    }
-
-    private static Class createInvocationClass( ClassLoader classLoader, Method interfaceMethod )
-            throws CannotCompileException
-    {
-        Class invocationClass;
-        final CtClass ctClass = JavassistUtils.createClass(
-                getSimpleName(interfaceMethod.getDeclaringClass()) + "_" + interfaceMethod.getName() +
-                        "_invocation",
-                JavassistInvocation.class);
-        final CtConstructor constructor = new CtConstructor(
-                JavassistUtils.resolve(new Class[] {Method.class, Object.class, Object[].class}),
-                ctClass);
-        constructor.setBody("{\n\tsuper($$);\n}");
-        ctClass.addConstructor(constructor);
-        final CtMethod proceedMethod = new CtMethod(JavassistUtils.resolve(Object.class), "proceed",
-                JavassistUtils.resolve(new Class[0]), ctClass);
-        final Class[] argumentTypes = interfaceMethod.getParameterTypes();
-        final StringBuffer proceedBody = new StringBuffer("{\n");
-        if( !Void.TYPE.equals(interfaceMethod.getReturnType()) )
-        {
-            proceedBody.append("\treturn ");
-            if( interfaceMethod.getReturnType().isPrimitive() )
-            {
-                proceedBody.append("new ");
-                proceedBody.append(ProxyUtils.getWrapperClass(interfaceMethod.getReturnType()).getName());
-                proceedBody.append("( ");
-            }
-        }
-        else
-        {
-            proceedBody.append("\t");
-        }
-        proceedBody.append("( (");
-        proceedBody.append(ProxyUtils.getJavaClassName(interfaceMethod.getDeclaringClass()));
-        proceedBody.append(" )target ).");
-        proceedBody.append(interfaceMethod.getName());
-        proceedBody.append("(");
-        for( int i = 0; i < argumentTypes.length; ++i )
-        {
-            final Class argumentType = argumentTypes[i];
-            proceedBody.append(createCastExpression(argumentType, "arguments[" + i + "]"));
-            if( i != argumentTypes.length - 1 )
-            {
-                proceedBody.append(", ");
-            }
-        }
-        if( !Void.TYPE.equals(interfaceMethod.getReturnType()) && interfaceMethod.getReturnType().isPrimitive() )
-        {
-            proceedBody.append(") );\n");
-        }
-        else
-        {
-            proceedBody.append(");\n");
-        }
-        if( Void.TYPE.equals(interfaceMethod.getReturnType()) )
-        {
-            proceedBody.append("\treturn null;\n");
-        }
-        proceedBody.append("}");
-        final String body = proceedBody.toString();
-        proceedMethod.setBody(body);
-        ctClass.addMethod(proceedMethod);
-        invocationClass = ctClass.toClass(classLoader);
-        return invocationClass;
-    }
-
-    private static Map getClassCache( ClassLoader classLoader )
-    {
-        Map cache = ( Map ) loaderToClassCache.get(classLoader);
-        if( cache == null )
-        {
-            cache = new HashMap();
-            loaderToClassCache.put(classLoader, cache);
-        }
-        return cache;
-    }
-
-    /**
-     * Returns a method invocation class specifically coded to invoke the supplied interface method.
-     *
-     * @param classLoader     the classloader to use
-     * @param interfaceMethod the interface method
-     * @return a method invocation class specifically coded to invoke the supplied interface method
-     * @throws CannotCompileException if a compilation error occurs
-     */
-    synchronized static Class getMethodInvocationClass( ClassLoader classLoader,
-                                                        Method interfaceMethod )
-            throws CannotCompileException
-    {
-        final Map classCache = getClassCache(classLoader);
-        final String key = toClassCacheKey(interfaceMethod);
-        final WeakReference invocationClassRef = ( WeakReference ) classCache.get(key);
-        Class invocationClass;
-        if( invocationClassRef == null )
-        {
-            invocationClass = createInvocationClass(classLoader, interfaceMethod);
-            classCache.put(key, new WeakReference(invocationClass));
-        }
-        else
-        {
-            synchronized( invocationClassRef )
-            {
-                invocationClass = ( Class ) invocationClassRef.get();
-                if( invocationClass == null )
-                {
-                    invocationClass = createInvocationClass(classLoader, interfaceMethod);
-                    classCache.put(key, new WeakReference(invocationClass));
-                }
-            }
-        }
-        return invocationClass;
-    }
-
-    private static String getSimpleName( Class c )
-    {
-        final String name = c.getName();
-        final int ndx = name.lastIndexOf('.');
-        return ndx == -1 ? name : name.substring(ndx + 1);
-    }
-
-    private static String toClassCacheKey( Method method )
-    {
-        return String.valueOf(method);
-    }
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public JavassistInvocation( Method method, Object target, Object[] arguments )
-    {
-        this.method = method;
-        this.target = target;
-        this.arguments = arguments;
-    }
-
-//**********************************************************************************************************************
-// Invocation Implementation
-//**********************************************************************************************************************
-
-    public Object[] getArguments()
-    {
-        return arguments;
-    }
-
-    public Method getMethod()
-    {
-        return method;
-    }
-
-    public Object getProxy()
-    {
-        return target;
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.javassist;

+

+import javassist.CannotCompileException;

+import javassist.CtClass;

+import javassist.CtConstructor;

+import javassist.CtMethod;

+import org.apache.commons.lang3.ArrayUtils;

+import org.apache.commons.proxy2.Invocation;

+import org.apache.commons.proxy2.ProxyUtils;

+

+import java.lang.ref.WeakReference;

+import java.lang.reflect.Method;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.WeakHashMap;

+

+/**

+ * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link Invocation} implementation.  This

+ * class actually serves as the superclass for all <a href="http://www.jboss.org/products/javassist">Javassist</a>-based

+ * method invocations.  Subclasses are dynamically created to deal with specific interface methods (they're hard-wired).

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+public abstract class JavassistInvocation implements Invocation

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private static WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>> loaderToClassCache = new WeakHashMap<ClassLoader, Map<String,WeakReference<Class<?>>>>();

+

+    /** The proxy object */

+    private final Object proxy;

+

+    /** The target object */

+    private final Object target;

+

+    /** The invoked method */

+    private final Method method;

+

+    /** The method arguments */

+    private final Object[] arguments;

+

+//**********************************************************************************************************************

+// Static Methods

+//**********************************************************************************************************************

+

+    private static String createCastExpression( Class<?> type, String objectToCast )

+    {

+        if( !type.isPrimitive() )

+        {

+            return "( " + ProxyUtils.getJavaClassName(type) + " )" + objectToCast;

+        }

+        else

+        {

+            return "( ( " + ProxyUtils.getWrapperClass(type).getName() + " )" + objectToCast + " )." +

+                    type.getName() + "Value()";

+        }

+    }

+

+    private static Class<?> createInvocationClass( ClassLoader classLoader, Method interfaceMethod )

+            throws CannotCompileException

+    {

+        Class<?> invocationClass;

+        final CtClass ctClass = JavassistUtils.createClass(

+                getSimpleName(interfaceMethod.getDeclaringClass()) + "_" + interfaceMethod.getName() +

+                        "_invocation",

+                JavassistInvocation.class);

+        final CtConstructor constructor = new CtConstructor(

+                JavassistUtils.resolve(new Class[] { Object.class, Object.class, Method.class, Object[].class }),

+                ctClass);

+        constructor.setBody("{\n\tsuper($$);\n}");

+        ctClass.addConstructor(constructor);

+        final CtMethod proceedMethod = new CtMethod(JavassistUtils.resolve(Object.class), "proceed",

+                JavassistUtils.resolve(new Class[0]), ctClass);

+        final Class<?>[] argumentTypes = interfaceMethod.getParameterTypes();

+        final StringBuilder proceedBody = new StringBuilder("{\n");

+        if( !Void.TYPE.equals(interfaceMethod.getReturnType()) )

+        {

+            proceedBody.append("\treturn ");

+            if( interfaceMethod.getReturnType().isPrimitive() )

+            {

+                proceedBody.append("new ");

+                proceedBody.append(ProxyUtils.getWrapperClass(interfaceMethod.getReturnType()).getName());

+                proceedBody.append("( ");

+            }

+        }

+        else

+        {

+            proceedBody.append("\t");

+        }

+        proceedBody.append("( (");

+        proceedBody.append(ProxyUtils.getJavaClassName(interfaceMethod.getDeclaringClass()));

+        proceedBody.append(" )getTarget() ).");

+        proceedBody.append(interfaceMethod.getName());

+        proceedBody.append("(");

+        for( int i = 0; i < argumentTypes.length; ++i )

+        {

+            final Class<?> argumentType = argumentTypes[i];

+            proceedBody.append(createCastExpression(argumentType, "getArguments()[" + i + "]"));

+            if( i != argumentTypes.length - 1 )

+            {

+                proceedBody.append(", ");

+            }

+        }

+        if( !Void.TYPE.equals(interfaceMethod.getReturnType()) && interfaceMethod.getReturnType().isPrimitive() )

+        {

+            proceedBody.append(") );\n");

+        }

+        else

+        {

+            proceedBody.append(");\n");

+        }

+        if( Void.TYPE.equals(interfaceMethod.getReturnType()) )

+        {

+            proceedBody.append("\treturn null;\n");

+        }

+        proceedBody.append("}");

+        final String body = proceedBody.toString();

+        proceedMethod.setBody(body);

+        ctClass.addMethod(proceedMethod);

+        invocationClass = ctClass.toClass(classLoader);

+        return invocationClass;

+    }

+

+    private static Map<String, WeakReference<Class<?>>> getClassCache( ClassLoader classLoader )

+    {

+        Map<String, WeakReference<Class<?>>> cache = loaderToClassCache.get(classLoader);

+        if( cache == null )

+        {

+            cache = new HashMap<String, WeakReference<Class<?>>>();

+            loaderToClassCache.put(classLoader, cache);

+        }

+        return cache;

+    }

+

+    /**

+     * Returns a method invocation class specifically coded to invoke the supplied interface method.

+     *

+     * @param classLoader     the classloader to use

+     * @param interfaceMethod the interface method

+     * @return a method invocation class specifically coded to invoke the supplied interface method

+     * @throws CannotCompileException if a compilation error occurs

+     */

+    static synchronized Class<?> getMethodInvocationClass( ClassLoader classLoader,

+                                                        Method interfaceMethod )

+            throws CannotCompileException

+    {

+        final Map<String, WeakReference<Class<?>>> classCache = getClassCache(classLoader);

+        final String key = toClassCacheKey(interfaceMethod);

+        final WeakReference<Class<?>> invocationClassRef = classCache.get(key);

+        Class<?> invocationClass;

+        if( invocationClassRef == null )

+        {

+            invocationClass = createInvocationClass(classLoader, interfaceMethod);

+            classCache.put(key, new WeakReference<Class<?>>(invocationClass));

+        }

+        else

+        {

+            synchronized( invocationClassRef )

+            {

+                invocationClass = invocationClassRef.get();

+                if( invocationClass == null )

+                {

+                    invocationClass = createInvocationClass(classLoader, interfaceMethod);

+                    classCache.put(key, new WeakReference<Class<?>>(invocationClass));

+                }

+            }

+        }

+        return invocationClass;

+    }

+

+    private static String getSimpleName( Class<?> c )

+    {

+        final String name = c.getName();

+        final int ndx = name.lastIndexOf('.');

+        return ndx == -1 ? name : name.substring(ndx + 1);

+    }

+

+    private static String toClassCacheKey( Method method )

+    {

+        return String.valueOf(method);

+    }

+

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+

+    protected JavassistInvocation( Object proxy, Object target, Method method, Object[] arguments )

+    {

+        this.proxy = proxy;

+        this.target = target;

+        this.method = method;

+        this.arguments = ArrayUtils.clone(arguments);

+    }

+

+//**********************************************************************************************************************

+// Invocation Implementation

+//**********************************************************************************************************************

+    protected final Object getTarget()

+    {

+        return target;

+    }

+

+    public Object[] getArguments()

+    {

+        return arguments;

+    }

+

+    public Method getMethod()

+    {

+        return method;

+    }

+

+    public Object getProxy()

+    {

+        return proxy;

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java b/javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistProxyFactory.java
similarity index 73%
rename from src/main/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java
rename to javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistProxyFactory.java
index 7a532f2..4ba823e 100644
--- a/src/main/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java
+++ b/javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistProxyFactory.java
@@ -1,282 +1,285 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.javassist;
-
-import javassist.CannotCompileException;
-import javassist.CtClass;
-import javassist.CtConstructor;
-import javassist.CtMethod;
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invoker;
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.exception.ProxyFactoryException;
-import org.apache.commons.proxy.factory.util.AbstractProxyClassGenerator;
-import org.apache.commons.proxy.factory.util.AbstractSubclassingProxyFactory;
-import org.apache.commons.proxy.factory.util.ProxyClassCache;
-
-import java.lang.reflect.Method;
-
-/**
- * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link org.apache.commons.proxy.ProxyFactory}
- * implementation.
- * <p/>
- * <b>Dependencies</b>: <ul> <li>Javassist version 3.0 or greater</li> </ul> </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class JavassistProxyFactory extends AbstractSubclassingProxyFactory
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod";
-
-    private static final ProxyClassCache delegatingProxyClassCache = new ProxyClassCache(
-            new DelegatingProxyClassGenerator());
-    private static final ProxyClassCache interceptorProxyClassCache = new ProxyClassCache(
-            new InterceptorProxyClassGenerator());
-    private static final ProxyClassCache invocationHandlerProxyClassCache = new ProxyClassCache(
-            new InvokerProxyClassGenerator());
-
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    private static void addGetMethodMethod(CtClass proxyClass) throws CannotCompileException
-    {
-        final CtMethod method = new CtMethod(JavassistUtils.resolve(Method.class), GET_METHOD_METHOD_NAME,
-                JavassistUtils.resolve(new Class[]{String.class, String.class, Class[].class}), proxyClass);
-        final String body = "try { return Class.forName($1).getMethod($2, $3); } catch( Exception e ) " +
-                "{ throw new RuntimeException(\"Unable to look up method.\", e); }";
-        method.setBody(body);
-        proxyClass.addMethod(method);
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public Object createDelegatorProxy(ClassLoader classLoader, ObjectProvider targetProvider,
-                                       Class[] proxyClasses)
-    {
-        try
-        {
-            final Class clazz = delegatingProxyClassCache.getProxyClass(classLoader, proxyClasses);
-            return clazz.getConstructor(new Class[]{ObjectProvider.class})
-                    .newInstance(new Object[]{targetProvider});
-        }
-        catch (Exception e)
-        {
-            throw new ProxyFactoryException("Unable to instantiate proxy from generated proxy class.", e);
-        }
-    }
-
-    public Object createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
-                                         Class[] proxyClasses)
-    {
-        try
-        {
-            final Class clazz = interceptorProxyClassCache.getProxyClass(classLoader, proxyClasses);
-            return clazz.getConstructor(new Class[]{Object.class, Interceptor.class})
-                    .newInstance(new Object[]{target, interceptor});
-        }
-        catch (Exception e)
-        {
-            throw new ProxyFactoryException("Unable to instantiate proxy class instance.", e);
-        }
-    }
-
-    public Object createInvokerProxy(ClassLoader classLoader, Invoker invoker,
-                                     Class[] proxyClasses)
-    {
-        try
-        {
-            final Class clazz = invocationHandlerProxyClassCache.getProxyClass(classLoader, proxyClasses);
-            return clazz.getConstructor(new Class[]{Invoker.class})
-                    .newInstance(new Object[]{invoker});
-        }
-        catch (Exception e)
-        {
-            throw new ProxyFactoryException("Unable to instantiate proxy from generated proxy class.", e);
-        }
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator
-    {
-        public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
-        {
-            try
-            {
-                final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
-                JavassistUtils.addField(ObjectProvider.class, "provider", proxyClass);
-                final CtConstructor proxyConstructor = new CtConstructor(
-                        JavassistUtils.resolve(new Class[]{ObjectProvider.class}),
-                        proxyClass);
-                proxyConstructor.setBody("{ this.provider = $1; }");
-                proxyClass.addConstructor(proxyConstructor);
-                JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
-                addHashCodeMethod(proxyClass);
-                addEqualsMethod(proxyClass);
-                final Method[] methods = getImplementationMethods(proxyClasses);
-                for (int i = 0; i < methods.length; ++i)
-                {
-                    if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
-                    {
-                        final Method method = methods[i];
-                        final CtMethod ctMethod = new CtMethod(JavassistUtils.resolve(method.getReturnType()),
-                                method.getName(),
-                                JavassistUtils.resolve(method.getParameterTypes()),
-                                proxyClass);
-                        final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() +
-                                " )provider.getObject() )." +
-                                method.getName() + "($$); }";
-                        ctMethod.setBody(body);
-                        proxyClass.addMethod(ctMethod);
-                    }
-                }
-                return proxyClass.toClass(classLoader);
-            }
-            catch (CannotCompileException e)
-            {
-                throw new ProxyFactoryException("Could not compile class.", e);
-            }
-        }
-    }
-
-    private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator
-    {
-        public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
-        {
-            try
-            {
-                final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
-                final Method[] methods = getImplementationMethods(proxyClasses);
-                JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
-                JavassistUtils.addField(Object.class, "target", proxyClass);
-                JavassistUtils.addField(Interceptor.class, "interceptor", proxyClass);
-                addGetMethodMethod(proxyClass);
-                addHashCodeMethod(proxyClass);
-                addEqualsMethod(proxyClass);
-                final CtConstructor proxyConstructor = new CtConstructor(
-                        JavassistUtils.resolve(
-                                new Class[]{Object.class, Interceptor.class}),
-                        proxyClass);
-                proxyConstructor
-                        .setBody(
-                                "{\n\tthis.target = $1;\n\tthis.interceptor = $2; }");
-                proxyClass.addConstructor(proxyConstructor);
-                for (int i = 0; i < methods.length; ++i)
-                {
-                    if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
-                    {
-                        final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
-                                methods[i].getName(),
-                                JavassistUtils.resolve(methods[i].getParameterTypes()),
-                                proxyClass);
-                        final Class invocationClass = JavassistInvocation
-                                .getMethodInvocationClass(classLoader, methods[i]);
-
-                        final String body = "{\n\t return ( $r ) interceptor.intercept( new " + invocationClass.getName() +
-                                "( " + GET_METHOD_METHOD_NAME + "(\"" + methods[i].getDeclaringClass().getName() +
-                                "\", \"" + methods[i].getName() + "\", $sig), target, $args ) );\n }";
-                        method.setBody(body);
-                        proxyClass.addMethod(method);
-                    }
-
-                }
-                return proxyClass.toClass(classLoader);
-            }
-            catch (CannotCompileException e)
-            {
-                throw new ProxyFactoryException("Could not compile class.", e);
-            }
-        }
-
-
-    }
-
-    private static void addEqualsMethod(CtClass proxyClass)
-            throws CannotCompileException
-    {
-        final CtMethod equalsMethod = new CtMethod(JavassistUtils.resolve(Boolean.TYPE), "equals",
-                JavassistUtils.resolve(new Class[]{Object.class}), proxyClass);
-        final String body = "{\n\treturn this == $1;\n}";
-        equalsMethod.setBody(body);
-        proxyClass.addMethod(equalsMethod);
-    }
-
-    private static void addHashCodeMethod(CtClass proxyClass)
-            throws CannotCompileException
-    {
-        final CtMethod hashCodeMethod = new CtMethod(JavassistUtils.resolve(Integer.TYPE), "hashCode",
-                new CtClass[0], proxyClass);
-        hashCodeMethod.setBody("{\n\treturn System.identityHashCode(this);\n}");
-        proxyClass.addMethod(hashCodeMethod);
-    }
-
-    private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator
-    {
-        public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
-        {
-            try
-            {
-                final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
-                final Method[] methods = getImplementationMethods(proxyClasses);
-                JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
-                JavassistUtils.addField(Invoker.class, "invoker", proxyClass);
-                final CtConstructor proxyConstructor = new CtConstructor(
-                        JavassistUtils.resolve(
-                                new Class[]{Invoker.class}),
-                        proxyClass);
-                proxyConstructor
-                        .setBody("{\n\tthis.invoker = $1; }");
-                proxyClass.addConstructor(proxyConstructor);
-                addGetMethodMethod(proxyClass);
-                addHashCodeMethod(proxyClass);
-                addEqualsMethod(proxyClass);
-                for (int i = 0; i < methods.length; ++i)
-                {
-                    if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
-                    {
-                        final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
-                                methods[i].getName(),
-                                JavassistUtils.resolve(methods[i].getParameterTypes()),
-                                proxyClass);
-                        final String body = "{\n\t return ( $r ) invoker.invoke( this, " + GET_METHOD_METHOD_NAME + "(\"" +
-                                methods[i].getDeclaringClass().getName() +
-                                "\", \"" + methods[i].getName() + "\", $sig), $args );\n }";
-                        method.setBody(body);
-                        proxyClass.addMethod(method);
-                    }
-                }
-                return proxyClass.toClass(classLoader);
-            }
-            catch (CannotCompileException e)
-            {
-                throw new ProxyFactoryException("Could not compile class.", e);
-            }
-        }
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.javassist;

+

+import javassist.CannotCompileException;

+import javassist.CtClass;

+import javassist.CtConstructor;

+import javassist.CtMethod;

+import org.apache.commons.proxy2.Interceptor;

+import org.apache.commons.proxy2.Invoker;

+import org.apache.commons.proxy2.ObjectProvider;

+import org.apache.commons.proxy2.ProxyUtils;

+import org.apache.commons.proxy2.exception.ProxyFactoryException;

+import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator;

+import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory;

+import org.apache.commons.proxy2.impl.ProxyClassCache;

+

+import java.lang.reflect.Method;

+

+public class JavassistProxyFactory extends AbstractSubclassingProxyFactory

+{

+    //**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod";

+

+    private static final ProxyClassCache DELEGATING_PROXY_CACHE = new ProxyClassCache(

+            new DelegatingProxyClassGenerator());

+    private static final ProxyClassCache INTERCEPTOR_PROXY_CACHE = new ProxyClassCache(

+            new InterceptorProxyClassGenerator());

+    private static final ProxyClassCache INVOKER_PROXY_CACHE = new ProxyClassCache(

+            new InvokerProxyClassGenerator());

+

+//**********************************************************************************************************************

+// Static Methods

+//**********************************************************************************************************************

+

+    private static void addGetMethodMethod(CtClass proxyClass) throws CannotCompileException

+    {

+        final CtMethod method = new CtMethod(JavassistUtils.resolve(Method.class), GET_METHOD_METHOD_NAME,

+                JavassistUtils.resolve(new Class[]{String.class, String.class, Class[].class}), proxyClass);

+        final String body = "try { return Class.forName($1).getMethod($2, $3); } catch( Exception e ) " +

+                "{ throw new RuntimeException(\"Unable to look up method.\", e); }";

+        method.setBody(body);

+        proxyClass.addMethod(method);

+    }

+

+    //**********************************************************************************************************************

+    // ProxyFactory Implementation

+    //**********************************************************************************************************************

+

+    /**

+     * {@inheritDoc}

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> targetProvider,

+                                       Class<?>... proxyClasses)

+    {

+        try

+        {

+            final Class<? extends T> clazz = (Class<? extends T>) DELEGATING_PROXY_CACHE.getProxyClass(classLoader, proxyClasses);

+            return clazz.getConstructor(ObjectProvider.class)

+                    .newInstance(targetProvider);

+        }

+        catch (Exception e)

+        {

+            throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 class.", e);

+        }

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,

+                                         Class<?>... proxyClasses)

+    {

+        try

+        {

+            final Class<? extends T> clazz = (Class<? extends T>) INTERCEPTOR_PROXY_CACHE.getProxyClass(classLoader, proxyClasses);

+            return clazz.getConstructor(Object.class, Interceptor.class)

+                    .newInstance(target, interceptor);

+        }

+        catch (Exception e)

+        {

+            throw new ProxyFactoryException("Unable to instantiate proxy2 class instance.", e);

+        }

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker,

+                                     Class<?>... proxyClasses)

+    {

+        try

+        {

+            final Class<? extends T> clazz = (Class<? extends T>) INVOKER_PROXY_CACHE.getProxyClass(classLoader, proxyClasses);

+            return clazz.getConstructor(Invoker.class)

+                    .newInstance(invoker);

+        }

+        catch (Exception e)

+        {

+            throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 class.", e);

+        }

+    }

+

+//**********************************************************************************************************************

+// Inner Classes

+//**********************************************************************************************************************

+

+    private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator

+    {

+        public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses)

+        {

+            try

+            {

+                final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));

+                JavassistUtils.addField(ObjectProvider.class, "provider", proxyClass);

+                final CtConstructor proxyConstructor = new CtConstructor(

+                        JavassistUtils.resolve(new Class[]{ObjectProvider.class}),

+                        proxyClass);

+                proxyConstructor.setBody("{ this.provider = $1; }");

+                proxyClass.addConstructor(proxyConstructor);

+                JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));

+                addHashCodeMethod(proxyClass);

+                addEqualsMethod(proxyClass);

+                final Method[] methods = getImplementationMethods(proxyClasses);

+                for (int i = 0; i < methods.length; ++i)

+                {

+                    if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i]))

+                    {

+                        final Method method = methods[i];

+                        final CtMethod ctMethod = new CtMethod(JavassistUtils.resolve(method.getReturnType()),

+                                method.getName(),

+                                JavassistUtils.resolve(method.getParameterTypes()),

+                                proxyClass);

+                        final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() +

+                                " )provider.getObject() )." +

+                                method.getName() + "($$); }";

+                        ctMethod.setBody(body);

+                        proxyClass.addMethod(ctMethod);

+                    }

+                }

+                return proxyClass.toClass(classLoader);

+            }

+            catch (CannotCompileException e)

+            {

+                throw new ProxyFactoryException("Could not compile class.", e);

+            }

+        }

+    }

+

+    private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator

+    {

+        public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses)

+        {

+            try

+            {

+                final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));

+                final Method[] methods = getImplementationMethods(proxyClasses);

+                JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));

+                JavassistUtils.addField(Object.class, "target", proxyClass);

+                JavassistUtils.addField(Interceptor.class, "interceptor", proxyClass);

+                addGetMethodMethod(proxyClass);

+                addHashCodeMethod(proxyClass);

+                addEqualsMethod(proxyClass);

+                final CtConstructor proxyConstructor = new CtConstructor(

+                        JavassistUtils.resolve(

+                                new Class[]{Object.class, Interceptor.class}),

+                        proxyClass);

+                proxyConstructor

+                        .setBody(

+                                "{\n\tthis.target = $1;\n\tthis.interceptor = $2; }");

+                proxyClass.addConstructor(proxyConstructor);

+                for (int i = 0; i < methods.length; ++i)

+                {

+                    if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i]))

+                    {

+                        final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),

+                                methods[i].getName(),

+                                JavassistUtils.resolve(methods[i].getParameterTypes()),

+                                proxyClass);

+                        final Class<?> invocationClass = JavassistInvocation

+                                .getMethodInvocationClass(classLoader, methods[i]);

+

+                        final String body = "{\n\t return ( $r ) interceptor.intercept( new " + invocationClass.getName() +

+                                "( this, target, " + GET_METHOD_METHOD_NAME + "(\"" + methods[i].getDeclaringClass().getName() +

+                                "\", \"" + methods[i].getName() + "\", $sig), $args ) );\n }";

+                        method.setBody(body);

+                        proxyClass.addMethod(method);

+                    }

+

+                }

+                return proxyClass.toClass(classLoader);

+            }

+            catch (CannotCompileException e)

+            {

+                throw new ProxyFactoryException("Could not compile class.", e);

+            }

+        }

+

+

+    }

+

+    private static void addEqualsMethod(CtClass proxyClass)

+            throws CannotCompileException

+    {

+        final CtMethod equalsMethod = new CtMethod(JavassistUtils.resolve(Boolean.TYPE), "equals",

+                JavassistUtils.resolve(new Class[]{Object.class}), proxyClass);

+        final String body = "{\n\treturn this == $1;\n}";

+        equalsMethod.setBody(body);

+        proxyClass.addMethod(equalsMethod);

+    }

+

+    private static void addHashCodeMethod(CtClass proxyClass)

+            throws CannotCompileException

+    {

+        final CtMethod hashCodeMethod = new CtMethod(JavassistUtils.resolve(Integer.TYPE), "hashCode",

+                new CtClass[0], proxyClass);

+        hashCodeMethod.setBody("{\n\treturn System.identityHashCode(this);\n}");

+        proxyClass.addMethod(hashCodeMethod);

+    }

+

+    private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator

+    {

+        public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses)

+        {

+            try

+            {

+                final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));

+                final Method[] methods = getImplementationMethods(proxyClasses);

+                JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));

+                JavassistUtils.addField(Invoker.class, "invoker", proxyClass);

+                final CtConstructor proxyConstructor = new CtConstructor(

+                        JavassistUtils.resolve(

+                                new Class[]{Invoker.class}),

+                        proxyClass);

+                proxyConstructor

+                        .setBody("{\n\tthis.invoker = $1; }");

+                proxyClass.addConstructor(proxyConstructor);

+                addGetMethodMethod(proxyClass);

+                addHashCodeMethod(proxyClass);

+                addEqualsMethod(proxyClass);

+                for (int i = 0; i < methods.length; ++i)

+                {

+                    if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i]))

+                    {

+                        final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),

+                                methods[i].getName(),

+                                JavassistUtils.resolve(methods[i].getParameterTypes()),

+                                proxyClass);

+                        final String body = "{\n\t return ( $r ) invoker.invoke( this, " + GET_METHOD_METHOD_NAME + "(\"" +

+                                methods[i].getDeclaringClass().getName() +

+                                "\", \"" + methods[i].getName() + "\", $sig), $args );\n }";

+                        method.setBody(body);

+                        proxyClass.addMethod(method);

+                    }

+                }

+                return proxyClass.toClass(classLoader);

+            }

+            catch (CannotCompileException e)

+            {

+                throw new ProxyFactoryException("Could not compile class.", e);

+            }

+        }

+    }

+}

diff --git a/src/main/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java b/javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistUtils.java
similarity index 71%
rename from src/main/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java
rename to javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistUtils.java
index 1b248b4..78dc74e 100644
--- a/src/main/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java
+++ b/javassist/src/main/java/org/apache/commons/proxy2/javassist/JavassistUtils.java
@@ -1,155 +1,153 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.javassist;
-
-import javassist.CannotCompileException;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.CtField;
-import javassist.LoaderClassPath;
-import javassist.NotFoundException;
-import org.apache.commons.proxy.ProxyUtils;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Some utility methods for dealing with Javassist.  This class is not part of the public API!
- *
- * @author James Carman
- * @since 1.0
- */
-class JavassistUtils
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    public static final String DEFAULT_BASE_NAME = "JavassistUtilsGenerated";
-    private static int classNumber = 0;
-    private static final ClassPool classPool = new ClassPool();
-
-    private static final Set classLoaders = new HashSet();
-
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    static
-    {
-        classPool.appendClassPath(new LoaderClassPath(ClassLoader.getSystemClassLoader()));
-    }
-
-    /**
-     * Adds a field to a class.
-     *
-     * @param fieldType      the field's type
-     * @param fieldName      the field name
-     * @param enclosingClass the class receiving the new field
-     * @throws CannotCompileException if a compilation problem occurs
-     */
-    public static void addField( Class fieldType, String fieldName, CtClass enclosingClass )
-            throws CannotCompileException
-    {
-        enclosingClass.addField(new CtField(resolve(fieldType), fieldName, enclosingClass));
-    }
-
-    /**
-     * Adds interfaces to a {@link CtClass}
-     *
-     * @param ctClass      the {@link CtClass}
-     * @param proxyClasses the interfaces
-     */
-    public static void addInterfaces( CtClass ctClass, Class[] proxyClasses )
-    {
-        for( int i = 0; i < proxyClasses.length; i++ )
-        {
-            Class proxyInterface = proxyClasses[i];
-            ctClass.addInterface(resolve(proxyInterface));
-        }
-    }
-
-    /**
-     * Creates a new {@link CtClass} derived from the Java {@link Class} using the default base name.
-     *
-     * @param superclass the superclass
-     * @return the new derived {@link CtClass}
-     */
-    public static CtClass createClass( Class superclass )
-    {
-        return createClass(DEFAULT_BASE_NAME, superclass);
-    }
-
-    /**
-     * Creates a new {@link CtClass} derived from the Java {@link Class} using the supplied base name.
-     *
-     * @param baseName   the base name
-     * @param superclass the superclass
-     * @return the new derived {@link CtClass}
-     */
-    public synchronized static CtClass createClass( String baseName, Class superclass )
-    {
-        return classPool.makeClass(baseName + "_" + classNumber++, resolve(superclass));
-    }
-
-    /**
-     * Finds the {@link CtClass} corresponding to the Java {@link Class} passed in.
-     *
-     * @param clazz the Java {@link Class}
-     * @return the {@link CtClass}
-     */
-    public static CtClass resolve( Class clazz )
-    {
-        synchronized( classLoaders )
-        {
-            try
-            {
-                final ClassLoader loader = clazz.getClassLoader();
-                if( loader != null && !classLoaders.contains(loader) )
-                {
-                    classLoaders.add(loader);
-                    classPool.appendClassPath(new LoaderClassPath(loader));
-                }
-                return classPool.get(ProxyUtils.getJavaClassName(clazz));
-            }
-            catch( NotFoundException e )
-            {
-                throw new ObjectProviderException(
-                        "Unable to find class " + clazz.getName() + " in default Javassist class pool.", e);
-            }
-        }
-    }
-
-    /**
-     * Resolves an array of Java {@link Class}es to an array of their corresponding {@link CtClass}es.
-     *
-     * @param classes the Java {@link Class}es
-     * @return the corresponding {@link CtClass}es
-     */
-    public static CtClass[] resolve( Class[] classes )
-    {
-        final CtClass[] ctClasses = new CtClass[classes.length];
-        for( int i = 0; i < ctClasses.length; ++i )
-        {
-            ctClasses[i] = resolve(classes[i]);
-        }
-        return ctClasses;
-    }
-}
-
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.javassist;

+

+import javassist.*;

+import org.apache.commons.proxy2.ProxyUtils;

+import org.apache.commons.proxy2.exception.ObjectProviderException;

+

+import java.util.HashSet;

+import java.util.Set;

+import java.util.concurrent.atomic.AtomicInteger;

+

+/**

+ * Some utility methods for dealing with Javassist.  This class is not part of the public API!

+ *

+ * @author James Carman

+ * @since 1.0

+ */

+final class JavassistUtils

+{

+//**********************************************************************************************************************

+// Fields

+//**********************************************************************************************************************

+

+    public static final String DEFAULT_BASE_NAME = "JavassistUtilsGenerated";

+    private static final AtomicInteger CLASS_NUMBER = new AtomicInteger(0);

+    private static final ClassPool CLASS_POOL = new ClassPool();

+    private static final Set<ClassLoader> CLASS_LOADERS = new HashSet<ClassLoader>();

+

+//**********************************************************************************************************************

+// Static Methods

+//**********************************************************************************************************************

+

+    static

+    {

+        CLASS_POOL.appendClassPath(new LoaderClassPath(ClassLoader.getSystemClassLoader()));

+    }

+

+    /**

+     * Adds a field to a class.

+     *

+     * @param fieldType      the field's type

+     * @param fieldName      the field name

+     * @param enclosingClass the class receiving the new field

+     * @throws CannotCompileException if a compilation problem occurs

+     */

+    public static void addField( Class<?> fieldType, String fieldName, CtClass enclosingClass )

+            throws CannotCompileException

+    {

+        enclosingClass.addField(new CtField(resolve(fieldType), fieldName, enclosingClass));

+    }

+

+    /**

+     * Adds interfaces to a {@link CtClass}

+     *

+     * @param ctClass      the {@link CtClass}

+     * @param proxyClasses the interfaces

+     */

+    public static void addInterfaces( CtClass ctClass, Class<?>[] proxyClasses )

+    {

+        for( int i = 0; i < proxyClasses.length; i++ )

+        {

+            Class<?> proxyInterface = proxyClasses[i];

+            ctClass.addInterface(resolve(proxyInterface));

+        }

+    }

+

+    /**

+     * Creates a new {@link CtClass} derived from the Java {@link Class} using the default base name.

+     *

+     * @param superclass the superclass

+     * @return the new derived {@link CtClass}

+     */

+    public static CtClass createClass( Class<?> superclass )

+    {

+        return createClass(DEFAULT_BASE_NAME, superclass);

+    }

+

+    /**

+     * Creates a new {@link CtClass} derived from the Java {@link Class} using the supplied base name.

+     *

+     * @param baseName   the base name

+     * @param superclass the superclass

+     * @return the new derived {@link CtClass}

+     */

+    public static synchronized CtClass createClass( String baseName, Class<?> superclass )

+    {

+        return CLASS_POOL.makeClass(baseName + "_" + CLASS_NUMBER.incrementAndGet(), resolve(superclass));

+    }

+

+    /**

+     * Finds the {@link CtClass} corresponding to the Java {@link Class} passed in.

+     *

+     * @param clazz the Java {@link Class}

+     * @return the {@link CtClass}

+     */

+    public static CtClass resolve( Class<?> clazz )

+    {

+        synchronized(CLASS_LOADERS)

+        {

+            try

+            {

+                final ClassLoader loader = clazz.getClassLoader();

+                if( loader != null && !CLASS_LOADERS.contains(loader) )

+                {

+                    CLASS_LOADERS.add(loader);

+                    CLASS_POOL.appendClassPath(new LoaderClassPath(loader));

+                }

+                return CLASS_POOL.get(ProxyUtils.getJavaClassName(clazz));

+            }

+            catch( NotFoundException e )

+            {

+                throw new ObjectProviderException(

+                        "Unable to find class " + clazz.getName() + " in default Javassist class pool.", e);

+            }

+        }

+    }

+

+    /**

+     * Resolves an array of Java {@link Class}es to an array of their corresponding {@link CtClass}es.

+     *

+     * @param classes the Java {@link Class}es

+     * @return the corresponding {@link CtClass}es

+     */

+    public static CtClass[] resolve( Class<?>[] classes )

+    {

+        final CtClass[] ctClasses = new CtClass[classes.length];

+        for( int i = 0; i < ctClasses.length; ++i )

+        {

+            ctClasses[i] = resolve(classes[i]);

+        }

+        return ctClasses;

+    }

+

+    private JavassistUtils() {

+        // Hiding constructor in utility class!

+    }

+}

diff --git a/src/test/resources/log4j.properties b/javassist/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
similarity index 75%
copy from src/test/resources/log4j.properties
copy to javassist/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
index b981287..283b11a 100644
--- a/src/test/resources/log4j.properties
+++ b/javassist/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
@@ -1,20 +1,17 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You 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.
-#
-log4j.rootLogger=DEBUG, console
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern=%d{MM-dd@HH:mm:ss} %-5p (%c{1}) %3x - %m%n
\ No newline at end of file
+#

+# Licensed to the Apache Software Foundation (ASF) under one or more

+# contributor license agreements.  See the NOTICE file distributed with

+# this work for additional information regarding copyright ownership.

+# The ASF licenses this file to You 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.

+#

+org.apache.commons.proxy2.javassist.JavassistProxyFactory
\ No newline at end of file
diff --git a/javassist/src/site/markdown/index.md b/javassist/src/site/markdown/index.md
new file mode 100644
index 0000000..13391dc
--- /dev/null
+++ b/javassist/src/site/markdown/index.md
@@ -0,0 +1,27 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+-->
+
+## Commons Proxy Javassist
+
+Provides the [JavassistProxyFactory][] which uses the [Javassist][] library
+to create proxy classes. This proxy factory is capable of proxying concrete
+non-`final` types and can thus be considered a *subclassing* proxy factory.
+
+[JavassistProxyFactory]: apidocs/org/apache/commons/proxy2/javassist/JavassistProxyFactory.html
+[Javassist]: http://www.javassist.org
diff --git a/javassist/src/test/java/org/apache/commons/proxy2/javassist/JavassistProxyFactoryTest.java b/javassist/src/test/java/org/apache/commons/proxy2/javassist/JavassistProxyFactoryTest.java
new file mode 100644
index 0000000..9eeea9e
--- /dev/null
+++ b/javassist/src/test/java/org/apache/commons/proxy2/javassist/JavassistProxyFactoryTest.java
@@ -0,0 +1,10 @@
+package org.apache.commons.proxy2.javassist;

+

+import org.apache.commons.proxy2.AbstractSubclassingProxyFactoryTestCase;

+

+public class JavassistProxyFactoryTest extends AbstractSubclassingProxyFactoryTestCase

+{

+//**********************************************************************************************************************

+// Constructors

+//**********************************************************************************************************************

+}

diff --git a/jdk/pom.xml b/jdk/pom.xml
new file mode 100644
index 0000000..a6ef879
--- /dev/null
+++ b/jdk/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>commons-proxy2-parent</artifactId>
+        <groupId>org.apache.commons</groupId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>commons-proxy2-jdk</artifactId>
+    <name>Commons Proxy JDK Proxies Module</name>
+    <description>Interface-based proxies using the core JDK Proxy mechanism
+    </description>
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/jdk/src/main/java/org/apache/commons/proxy2/jdk/JdkProxyFactory.java b/jdk/src/main/java/org/apache/commons/proxy2/jdk/JdkProxyFactory.java
new file mode 100644
index 0000000..cc4ff4e
--- /dev/null
+++ b/jdk/src/main/java/org/apache/commons/proxy2/jdk/JdkProxyFactory.java
@@ -0,0 +1,225 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.jdk;

+

+import org.apache.commons.proxy2.*;

+import org.apache.commons.proxy2.impl.AbstractProxyFactory;

+

+import java.io.Serializable;

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.lang.reflect.Proxy;

+

+/**

+ * {@link ProxyFactory} implementation that uses {@link java.lang.reflect.Proxy} proxies.

+ */

+public class JdkProxyFactory extends AbstractProxyFactory

+{

+//**********************************************************************************************************************

+// ProxyFactory Implementation

+//**********************************************************************************************************************

+

+    /**

+     * Creates a proxy2 which delegates to the object provided by <code>delegateProvider</code>.

+     *

+     * @param classLoader      the class loader to use when generating the proxy2

+     * @param delegateProvider the delegate provider

+     * @param proxyClasses     the interfaces that the proxy2 should implement

+     * @return a proxy2 which delegates to the object provided by the target <code>delegateProvider>

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createDelegatorProxy( ClassLoader classLoader, ObjectProvider<?> delegateProvider,

+                                        Class<?>... proxyClasses )

+    {

+        return (T) Proxy.newProxyInstance(classLoader, proxyClasses,

+                                      new DelegatorInvocationHandler(delegateProvider));

+    }

+

+    /**

+     * Creates a proxy2 which passes through a {@link Interceptor interceptor} before eventually reaching the

+     * <code>target</code> object.

+     *

+     * @param classLoader  the class loader to use when generating the proxy2

+     * @param target       the target object

+     * @param interceptor  the method interceptor

+     * @param proxyClasses the interfaces that the proxy2 should implement.

+     * @return a proxy2 which passes through a {@link Interceptor interceptor} before eventually reaching the

+     *         <code>target</code> object.

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createInterceptorProxy( ClassLoader classLoader, Object target, Interceptor interceptor,

+                                          Class<?>... proxyClasses )

+    {

+        return (T) Proxy

+                .newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(target, interceptor));

+    }

+

+    /**

+     * Creates a proxy2 which uses the provided {@link Invoker} to handle all method invocations.

+     *

+     * @param classLoader  the class loader to use when generating the proxy2

+     * @param invoker      the invoker

+     * @param proxyClasses the interfaces that the proxy2 should implement

+     * @return a proxy2 which uses the provided {@link Invoker} to handle all method invocations

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createInvokerProxy( ClassLoader classLoader, Invoker invoker,

+                                      Class<?>... proxyClasses )

+    {

+        return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InvokerInvocationHandler(invoker));

+    }

+

+//**********************************************************************************************************************

+// Inner Classes

+//**********************************************************************************************************************

+

+    private abstract static class AbstractInvocationHandler implements InvocationHandler, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable

+        {

+            if( ProxyUtils.isHashCode(method) )

+            {

+                return System.identityHashCode(proxy);

+            }

+            else if( ProxyUtils.isEqualsMethod(method) )

+            {

+                return proxy == args[0];

+            }

+            else

+            {

+                return invokeImpl(proxy, method, args);

+            }

+        }

+

+        protected abstract Object invokeImpl( Object proxy, Method method, Object[] args ) throws Throwable;

+    }

+

+    private static class DelegatorInvocationHandler extends AbstractInvocationHandler

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final ObjectProvider<?> delegateProvider;

+

+        protected DelegatorInvocationHandler( ObjectProvider<?> delegateProvider )

+        {

+            this.delegateProvider = delegateProvider;

+        }

+

+        public Object invokeImpl( Object proxy, Method method, Object[] args ) throws Throwable

+        {

+            try

+            {

+                return method.invoke(delegateProvider.getObject(), args);

+            }

+            catch( InvocationTargetException e )

+            {

+                throw e.getTargetException();

+            }

+        }

+    }

+

+    private static class InterceptorInvocationHandler extends AbstractInvocationHandler

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final Object target;

+        private final Interceptor methodInterceptor;

+

+        public InterceptorInvocationHandler( Object target, Interceptor methodInterceptor )

+        {

+            this.target = target;

+            this.methodInterceptor = methodInterceptor;

+        }

+

+        public Object invokeImpl( Object proxy, Method method, Object[] args ) throws Throwable

+        {

+            final ReflectionInvocation invocation = new ReflectionInvocation(proxy, target, method, args);

+            return methodInterceptor.intercept(invocation);

+        }

+    }

+

+    private static class InvokerInvocationHandler extends AbstractInvocationHandler

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final Invoker invoker;

+

+        public InvokerInvocationHandler( Invoker invoker )

+        {

+            this.invoker = invoker;

+        }

+

+        public Object invokeImpl( Object proxy, Method method, Object[] args ) throws Throwable

+        {

+            return invoker.invoke(proxy, method, args);

+        }

+    }

+

+    private static class ReflectionInvocation implements Invocation, Serializable

+    {

+        /** Serialization version */

+        private static final long serialVersionUID = 1L;

+

+        private final Object proxy;

+        private final Object target;

+        private final Method method;

+        private final Object[] arguments;

+

+        public ReflectionInvocation( Object proxy, Object target, Method method, Object[] arguments )

+        {

+            this.proxy = proxy;

+            this.target = target;

+            this.method = method;

+            this.arguments = ( arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments );

+        }

+

+        public Object[] getArguments()

+        {

+            return arguments;

+        }

+

+        public Method getMethod()

+        {

+            return method;

+        }

+

+        public Object getProxy()

+        {

+            return proxy;

+        }

+

+        public Object proceed() throws Throwable

+        {

+            try

+            {

+                return method.invoke(target, arguments);

+            }

+            catch( InvocationTargetException e )

+            {

+                throw e.getTargetException();

+            }

+        }

+    }

+}

diff --git a/src/test/resources/log4j.properties b/jdk/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
similarity index 75%
copy from src/test/resources/log4j.properties
copy to jdk/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
index b981287..a27a58d 100644
--- a/src/test/resources/log4j.properties
+++ b/jdk/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory
@@ -1,20 +1,17 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You 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.
-#
-log4j.rootLogger=DEBUG, console
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern=%d{MM-dd@HH:mm:ss} %-5p (%c{1}) %3x - %m%n
\ No newline at end of file
+#

+# Licensed to the Apache Software Foundation (ASF) under one or more

+# contributor license agreements.  See the NOTICE file distributed with

+# this work for additional information regarding copyright ownership.

+# The ASF licenses this file to You 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.

+#

+org.apache.commons.proxy2.jdk.JdkProxyFactory
\ No newline at end of file
diff --git a/jdk/src/site/markdown/index.md b/jdk/src/site/markdown/index.md
new file mode 100644
index 0000000..200a724
--- /dev/null
+++ b/jdk/src/site/markdown/index.md
@@ -0,0 +1,26 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+-->
+
+## Commons Proxy JDK
+
+Uses the [Proxy][] mechanism provided by the core JDK to create
+dynamic proxy instances. This proxy factory exposes interfaces
+only.
+
+[Proxy]: http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html
diff --git a/jdk/src/test/java/org/apache/commons/proxy2/jdk/JdkProxyFactoryTest.java b/jdk/src/test/java/org/apache/commons/proxy2/jdk/JdkProxyFactoryTest.java
new file mode 100644
index 0000000..f157a25
--- /dev/null
+++ b/jdk/src/test/java/org/apache/commons/proxy2/jdk/JdkProxyFactoryTest.java
@@ -0,0 +1,7 @@
+package org.apache.commons.proxy2.jdk;

+

+import org.apache.commons.proxy2.AbstractProxyFactoryTestCase;

+

+public class JdkProxyFactoryTest extends AbstractProxyFactoryTestCase

+{

+}

diff --git a/pom.xml b/pom.xml
index ac007f6..4412469 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,35 +1,44 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
+
 <!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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
+  ~      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.
+  ~ 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.
   -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
+    <modules>
+        <module>core</module>
+        <module>jdk</module>
+        <module>asm4</module>
+        <module>javassist</module>
+        <module>cglib</module>
+        <module>build-tools</module>
+        <module>test</module>
+    </modules>
     <parent>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-parent</artifactId>
-        <version>32</version>
+        <version>25</version>
     </parent>
-    <artifactId>commons-proxy</artifactId>
-    <version>1.1-SNAPSHOT</version>
-    <name>Apache Commons Proxy</name>
+    <artifactId>commons-proxy2-parent</artifactId>
+    <version>2.0-SNAPSHOT</version>
+    <name>Commons Proxy Parent</name>
     <description>Java library for dynamic proxying</description>
-    <url>http://commons.apache.org/proper/commons-proxy/</url>
+    <url>http://commons.apache.org/proxy/</url>
     <inceptionYear>2005</inceptionYear>
 
     <issueManagement>
@@ -71,19 +80,17 @@
             </roles>
             <url></url>
         </developer>
+        <developer>
+            <name>Matt Benson</name>
+            <email>mbenson@apache.org</email>
+            <roles>
+                <role>developer</role>
+            </roles>
+        </developer>
     </developers>
 
     <contributors>
         <contributor>
-            <name>J&#246;rg Schaible</name>
-            <email>joerg.schaible@gmx.de</email>
-            <organization/>
-            <roles>
-                <role>advisor</role>
-            </roles>
-            <url></url>
-        </contributor>
-        <contributor>
             <name>Howard M. Lewis Ship</name>
             <email>hlship@apache.org</email>
             <organization/>
@@ -92,6 +99,20 @@
             </roles>
             <url>http://www.howardlewisship.com/</url>
         </contributor>
+        <contributor>
+            <name>J&#246;rg Schaible</name>
+            <email>joerg.schaible@gmx.de</email>
+            <organization/>
+            <roles>
+                <role>advisor</role>
+            </roles>
+        </contributor>
+        <contributor>
+            <name>Mark Struberg</name>
+        </contributor>
+        <contributor>
+            <name>Romain Manni-Bucau</name>
+        </contributor>
     </contributors>
 
     <scm>
@@ -101,6 +122,14 @@
     </scm>
 
     <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+        </resources>
         <plugins>
             <plugin>
                 <artifactId>maven-assembly-plugin</artifactId>
@@ -114,132 +143,59 @@
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-scm-publish-plugin</artifactId>
-                <configuration>
-                    <ignorePathsToDelete>
-                        <ignorePathToDelete>javadocs</ignorePathToDelete>
-                    </ignorePathsToDelete>
-                </configuration>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <dependencies>
+                    <dependency>
+                        <groupId>${project.groupId}</groupId>
+                        <artifactId>commons-proxy2-build-tools</artifactId>
+                        <version>${project.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.3</version>
+                <dependencies>
+                  <dependency>
+                    <groupId>org.apache.maven.doxia</groupId>
+                    <artifactId>doxia-module-markdown</artifactId>
+                    <version>1.3</version>
+                  </dependency>
+                </dependencies>
             </plugin>
         </plugins>
     </build>
 
-    <dependencies>
-        <dependency>
-            <groupId>cglib</groupId>
-            <artifactId>cglib-nodep</artifactId>
-            <version>2.1_3</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>jboss</groupId>
-            <artifactId>javassist</artifactId>
-            <version>3.0</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>aopalliance</groupId>
-            <artifactId>aopalliance</artifactId>
-            <version>1.0</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>axis</groupId>
-            <artifactId>axis-jaxrpc</artifactId>
-            <version>1.2.1</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>hessian</groupId>
-            <artifactId>hessian</artifactId>
-            <version>3.0.1</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>burlap</groupId>
-            <artifactId>burlap</artifactId>
-            <version>2.1.7</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>commons-logging</groupId>
-            <artifactId>commons-logging</artifactId>
-            <version>1.0.4</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <version>1.4.3</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>commons-collections</groupId>
-            <artifactId>commons-collections</artifactId>
-            <version>3.1</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>concurrent</groupId>
-            <artifactId>concurrent</artifactId>
-            <version>1.3.4</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>jmock</groupId>
-            <artifactId>jmock</artifactId>
-            <version>1.0.1</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>xmlrpc</groupId>
-            <artifactId>xmlrpc</artifactId>
-            <version>2.0</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>commons-codec</groupId>
-            <artifactId>commons-codec</artifactId>
-            <version>1.3</version>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>commons-lang</groupId>
-            <artifactId>commons-lang</artifactId>
-            <version>2.3</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
     <reporting>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>2.1</version>
                 <configuration>
-                    <configLocation>${basedir}/checkstyle.xml</configLocation>
-                    <enableRulesSummary>false</enableRulesSummary>
-                    <headerFile>${basedir}/license-header.txt</headerFile>
+                    <configLocation>org/apache/commons/proxy2/checkstyle.xml</configLocation>
+                    <headerLocation>org/apache/commons/proxy2/license-header.txt</headerLocation>
                 </configuration>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
-                <version>2.5</version>
+
                 <configuration>
                     <linksource>true</linksource>
                     <links>
-                        <link>http://download.oracle.com/javase/1.4.2/docs/api/</link>
+                        <link>http://java.sun.com/j2se/1.4.2/docs/api/</link>
                         <link>http://ws.apache.org/xmlrpc/apidocs/</link>
                         <link>http://www.csg.is.titech.ac.jp/~chiba/javassist/html</link>
-                        <link>http://aopalliance.sourceforge.net/doc/</link>
-                        <link>http://gee.cs.oswego.edu/dl/classes/</link>
-                        <link>http://commons.apache.org/logging/apidocs/</link>
-                        <link>http://www.slf4j.org/api/</link>
                     </links>
                 </configuration>
-
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-report-plugin</artifactId>
+                <configuration>
+                    <aggregate>true</aggregate>
+                </configuration>
             </plugin>
         </plugins>
     </reporting>
@@ -252,30 +208,54 @@
         </site>
     </distributionManagement>
 
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-lang3</artifactId>
+                <version>3.0</version>
+            </dependency>
+            <dependency>
+                <groupId>cglib</groupId>
+                <artifactId>cglib-nodep</artifactId>
+                <version>2.1_3</version>
+            </dependency>
+            <dependency>
+                <groupId>jboss</groupId>
+                <artifactId>javassist</artifactId>
+                <version>3.0</version>
+            </dependency>
+            <dependency>
+              <groupId>org.ow2.asm</groupId>
+              <artifactId>asm</artifactId>
+              <version>${asm.version}</version>
+            </dependency>
+            <dependency>
+              <groupId>org.ow2.asm</groupId>
+              <artifactId>asm-commons</artifactId>
+              <version>${asm.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>jmock</groupId>
+                <artifactId>jmock</artifactId>
+                <version>1.0.1</version>
+            </dependency>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.8.1</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
     <properties>
-        <maven.compiler.source>1.4</maven.compiler.source>
-        <maven.compiler.target>1.4</maven.compiler.target>
+        <maven.compile.source>1.6</maven.compile.source>
+        <maven.compile.target>1.6</maven.compile.target>
         <commons.componentid>proxy</commons.componentid>
         <commons.release.version>1.0</commons.release.version>
         <commons.binary.suffix></commons.binary.suffix>
         <commons.jira.id>PROXY</commons.jira.id>
         <commons.jira.pid>12310731</commons.jira.pid>
-        <releaseManager>${user.name}</releaseManager>
+        <asm.version>4.1</asm.version>
     </properties>
-
-    <profiles>
-        <profile>
-            <id>rc</id>
-            <distributionManagement>
-                <site>
-                    <id>stagingSite</id>
-                    <name>Release Candidate Staging Site</name>
-                    <url>
-                        ${commons.deployment.protocol}://people.apache.org/home/${releaseManager}/public_html/${artifactId}-${version}
-                    </url>
-                </site>
-            </distributionManagement>
-        </profile>
-    </profiles>
-
 </project>
diff --git a/src/assembly/bin.xml b/src/assembly/bin.xml
index 5762291..ba78bbd 100644
--- a/src/assembly/bin.xml
+++ b/src/assembly/bin.xml
@@ -1,19 +1,20 @@
 <!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements.  See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You 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
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
 
-      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.
--->
 <assembly>
     <id>bin</id>
     <formats>
diff --git a/src/assembly/src.xml b/src/assembly/src.xml
index 0d0ac18..5a7cc0d 100644
--- a/src/assembly/src.xml
+++ b/src/assembly/src.xml
@@ -1,19 +1,20 @@
 <!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements.  See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You 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
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
 
-      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.
--->
 <assembly>
     <id>src</id>
     <formats>
diff --git a/src/main/java/org/apache/commons/proxy/ProxyFactory.java b/src/main/java/org/apache/commons/proxy/ProxyFactory.java
deleted file mode 100644
index 3c4fc69..0000000
--- a/src/main/java/org/apache/commons/proxy/ProxyFactory.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy;
-
-import java.io.Serializable;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-/**
- * A <code>ProxyFactory</code> can be used to create three different &quot;flavors&quot; of proxy objects.
- * <p/>
- * <ul>
- * <li>Delegator - the proxy will delegate to an object provided by an {@link ObjectProvider}</li>
- * <li>Interceptor - the proxy will pass each method invocation through an {@link Interceptor}</li>
- * <li>Invoker - the proxy will allow an {@link Invoker} to handle all method invocations</li>
- * </ul>
- * <p/>
- * <p>
- * Originally, the ProxyFactory class was an interface.  However, to allow for future changes to the
- * class without breaking binary or semantic compatibility, it has been changed to a concrete class.
- * <p/>
- * </p>
- * <p>
- * <b>Note</b>: This class uses Java reflection.  For more efficient proxies, try using either
- * {@link org.apache.commons.proxy.factory.cglib.CglibProxyFactory CglibProxyFactory} or
- * {@link org.apache.commons.proxy.factory.javassist.JavassistProxyFactory JavassistProxyFactory} instead.
- * </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class ProxyFactory
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns true if all <code>proxyClasses</code> are interfaces.
-     *
-     * @param proxyClasses the proxy classes
-     * @return true if all <code>proxyClasses</code> are interfaces
-     */
-    public boolean canProxy(Class[] proxyClasses)
-    {
-        for (int i = 0; i < proxyClasses.length; i++)
-        {
-            Class proxyClass = proxyClasses[i];
-            if (!proxyClass.isInterface())
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.  The proxy will be
-     * generated using the current thread's "context class loader."
-     *
-     * @param delegateProvider the delegate provider
-     * @param proxyClasses     the interfaces that the proxy should implement
-     * @return a proxy which delegates to the object provided by the target object provider
-     */
-    public Object createDelegatorProxy(ObjectProvider delegateProvider, Class[] proxyClasses)
-    {
-        return createDelegatorProxy(Thread.currentThread().getContextClassLoader(), delegateProvider, proxyClasses);
-    }
-
-    /**
-     * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.
-     *
-     * @param classLoader      the class loader to use when generating the proxy
-     * @param delegateProvider the delegate provider
-     * @param proxyClasses     the interfaces that the proxy should implement
-     * @return a proxy which delegates to the object provided by the target <code>delegateProvider>
-     */
-    public Object createDelegatorProxy(ClassLoader classLoader, ObjectProvider delegateProvider,
-                                       Class[] proxyClasses)
-    {
-        return Proxy.newProxyInstance(classLoader, proxyClasses,
-                new DelegatorInvocationHandler(delegateProvider));
-    }
-
-    /**
-     * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
-     * <code>target</code> object.  The proxy will be generated using the current thread's "context class loader."
-     *
-     * @param target       the target object
-     * @param interceptor  the method interceptor
-     * @param proxyClasses the interfaces that the proxy should implement
-     * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
-     *         <code>target</code> object.
-     */
-    public Object createInterceptorProxy(Object target, Interceptor interceptor,
-                                         Class[] proxyClasses)
-    {
-        return createInterceptorProxy(Thread.currentThread().getContextClassLoader(), target, interceptor,
-                proxyClasses);
-    }
-
-    /**
-     * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
-     * <code>target</code> object.
-     *
-     * @param classLoader  the class loader to use when generating the proxy
-     * @param target       the target object
-     * @param interceptor  the method interceptor
-     * @param proxyClasses the interfaces that the proxy should implement.
-     * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
-     *         <code>target</code> object.
-     */
-    public Object createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
-                                         Class[] proxyClasses)
-    {
-        return Proxy
-                .newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(target, interceptor));
-    }
-
-    /**
-     * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.  The proxy will be
-     * generated using the current thread's "context class loader."
-     *
-     * @param invoker      the invoker
-     * @param proxyClasses the interfaces that the proxy should implement
-     * @return a proxy which uses the provided {@link Invoker} to handle all method invocations
-     */
-    public Object createInvokerProxy(Invoker invoker, Class[] proxyClasses)
-    {
-        return createInvokerProxy(Thread.currentThread().getContextClassLoader(), invoker,
-                proxyClasses);
-    }
-
-    /**
-     * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.
-     *
-     * @param classLoader  the class loader to use when generating the proxy
-     * @param invoker      the invoker
-     * @param proxyClasses the interfaces that the proxy should implement
-     * @return a proxy which uses the provided {@link Invoker} to handle all method invocations
-     */
-    public Object createInvokerProxy(ClassLoader classLoader, Invoker invoker,
-                                     Class[] proxyClasses)
-    {
-        return Proxy.newProxyInstance(classLoader, proxyClasses, new InvokerInvocationHandler(invoker));
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private static class DelegatorInvocationHandler extends AbstractInvocationHandler
-    {
-        private final ObjectProvider delegateProvider;
-
-        protected DelegatorInvocationHandler(ObjectProvider delegateProvider)
-        {
-            this.delegateProvider = delegateProvider;
-        }
-
-        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
-        {
-            try
-            {
-                return method.invoke(delegateProvider.getObject(), args);
-            }
-            catch (InvocationTargetException e)
-            {
-                throw e.getTargetException();
-            }
-        }
-    }
-
-    private static class InterceptorInvocationHandler extends AbstractInvocationHandler
-    {
-        private final Object target;
-        private final Interceptor methodInterceptor;
-
-        public InterceptorInvocationHandler(Object target, Interceptor methodInterceptor)
-        {
-            this.target = target;
-            this.methodInterceptor = methodInterceptor;
-        }
-
-        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
-        {
-            final ReflectionInvocation invocation = new ReflectionInvocation(target, method, args);
-            return methodInterceptor.intercept(invocation);
-        }
-    }
-
-    private abstract static class AbstractInvocationHandler implements InvocationHandler, Serializable
-    {
-        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
-        {
-            if (isHashCode(method))
-            {
-                return new Integer(System.identityHashCode(proxy));
-            }
-            else if (isEqualsMethod(method))
-            {
-                return Boolean.valueOf(proxy == args[0]);
-            }
-            else
-            {
-                return invokeImpl(proxy, method, args);
-            }
-        }
-
-        protected abstract Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable;
-    }
-
-    private static class InvokerInvocationHandler extends AbstractInvocationHandler
-    {
-        private final Invoker invoker;
-
-        public InvokerInvocationHandler(Invoker invoker)
-        {
-            this.invoker = invoker;
-        }
-
-        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
-        {
-            return invoker.invoke(proxy, method, args);
-        }
-    }
-
-    protected static boolean isHashCode(Method method)
-    {
-        return "hashCode".equals(method.getName()) &&
-                Integer.TYPE.equals(method.getReturnType()) &&
-                method.getParameterTypes().length == 0;
-    }
-
-    protected static boolean isEqualsMethod(Method method)
-    {
-        return "equals".equals(method.getName()) &&
-                Boolean.TYPE.equals(method.getReturnType()) &&
-                method.getParameterTypes().length == 1 &&
-                Object.class.equals(method.getParameterTypes()[0]);
-    }
-
-    private static class ReflectionInvocation implements Invocation, Serializable
-    {
-        private final Method method;
-        private final Object[] arguments;
-        private final Object target;
-
-        public ReflectionInvocation(Object target, Method method, Object[] arguments)
-        {
-            this.method = method;
-            this.arguments = (arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments);
-            this.target = target;
-        }
-
-        public Object[] getArguments()
-        {
-            return arguments;
-        }
-
-        public Method getMethod()
-        {
-            return method;
-        }
-
-        public Object getProxy()
-        {
-            return target;
-        }
-
-        public Object proceed() throws Throwable
-        {
-            try
-            {
-                return method.invoke(target, arguments);
-            }
-            catch (InvocationTargetException e)
-            {
-                throw e.getTargetException();
-            }
-        }
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/ProxyUtils.java b/src/main/java/org/apache/commons/proxy/ProxyUtils.java
deleted file mode 100644
index 28e2443..0000000
--- a/src/main/java/org/apache/commons/proxy/ProxyUtils.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy;
-
-import org.apache.commons.proxy.invoker.NullInvoker;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Provides some helpful proxy utility methods.
- *
- * @author James Carman
- * @since 1.0
- */
-public class ProxyUtils
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    public static final Object[] EMPTY_ARGUMENTS = new Object[0];
-    public static final Class[] EMPTY_ARGUMENT_TYPES = new Class[0];
-    private static final Map wrapperClassMap = new HashMap();
-    public static Map defaultValueMap = new HashMap();
-
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    static
-    {
-        wrapperClassMap.put(Integer.TYPE, Integer.class);
-        wrapperClassMap.put(Character.TYPE, Character.class);
-        wrapperClassMap.put(Boolean.TYPE, Boolean.class);
-        wrapperClassMap.put(Short.TYPE, Short.class);
-        wrapperClassMap.put(Long.TYPE, Long.class);
-        wrapperClassMap.put(Float.TYPE, Float.class);
-        wrapperClassMap.put(Double.TYPE, Double.class);
-        wrapperClassMap.put(Byte.TYPE, Byte.class);
-    }
-
-    static
-    {
-        defaultValueMap.put(Integer.TYPE, new Integer(0));
-        defaultValueMap.put(Long.TYPE, new Long(0));
-        defaultValueMap.put(Short.TYPE, new Short(( short ) 0));
-        defaultValueMap.put(Byte.TYPE, new Byte(( byte ) 0));
-        defaultValueMap.put(Float.TYPE, new Float(0.0f));
-        defaultValueMap.put(Double.TYPE, new Double(0.0));
-        defaultValueMap.put(Character.TYPE, new Character(( char ) 0));
-        defaultValueMap.put(Boolean.TYPE, Boolean.FALSE);
-    }
-
-    /**
-     * Creates a "null object" which implements the <code>proxyClasses</code>.
-     *
-     * @param proxyFactory the proxy factory to be used to create the proxy object
-     * @param proxyClasses the proxy interfaces
-     * @return a "null object" which implements the <code>proxyClasses</code>.
-     */
-    public static Object createNullObject( ProxyFactory proxyFactory, Class[] proxyClasses )
-    {
-        return proxyFactory.createInvokerProxy(new NullInvoker(), proxyClasses);
-    }
-
-    /**
-     * Creates a "null object" which implements the <code>proxyClasses</code>.
-     *
-     * @param proxyFactory the proxy factory to be used to create the proxy object
-     * @param classLoader  the class loader to be used by the proxy factory to create the proxy object
-     * @param proxyClasses the proxy interfaces
-     * @return a "null object" which implements the <code>proxyClasses</code>.
-     */
-    public static Object createNullObject( ProxyFactory proxyFactory, ClassLoader classLoader, Class[] proxyClasses )
-    {
-        return proxyFactory.createInvokerProxy(classLoader, new NullInvoker(), proxyClasses);
-    }
-
-    /**
-     * <p>Gets an array of {@link Class} objects representing all interfaces implemented by the given class and its
-     * superclasses.</p>
-     * <p/>
-     * <p>The order is determined by looking through each interface in turn as declared in the source file and following
-     * its hierarchy up. Then each superclass is considered in the same way. Later duplicates are ignored, so the order
-     * is maintained.</p>
-     * <p/>
-     * <b>Note</b>: Implementation of this method was "borrowed" from
-     * <a href="http://commons.apache.org/lang/">Apache Commons Lang</a> to avoid a dependency.</p>
-     *
-     * @param cls the class to look up, may be <code>null</code>
-     * @return an array of {@link Class} objects representing all interfaces implemented by the given class and its
-     *         superclasses or <code>null</code> if input class is null.
-     */
-    public static Class[] getAllInterfaces( Class cls )
-    {
-        final List interfaces = getAllInterfacesImpl(cls, new LinkedList());
-        return interfaces == null ? null : ( Class[] ) interfaces.toArray(new Class[interfaces.size()]);
-    }
-
-    private static List getAllInterfacesImpl( Class cls, List list )
-    {
-        if( cls == null )
-        {
-            return null;
-        }
-        while( cls != null )
-        {
-            Class[] interfaces = cls.getInterfaces();
-            for( int i = 0; i < interfaces.length; i++ )
-            {
-                if( !list.contains(interfaces[i]) )
-                {
-                    list.add(interfaces[i]);
-                }
-                getAllInterfacesImpl(interfaces[i], list);
-            }
-            cls = cls.getSuperclass();
-        }
-        return list;
-    }
-
-    /**
-     * Returns the class name as you would expect to see it in Java code.
-     * <p/>
-     * <b>Examples:</b> <ul> <li>getJavaClassName( Object[].class ) == "Object[]"</li> <li>getJavaClassName(
-     * Object[][].class ) == "Object[][]"</li> <li>getJavaClassName( Integer.TYPE ) == "int"</li> </p>
-     *
-     * @param clazz the class
-     * @return the class' name as you would expect to see it in Java code
-     */
-    public static String getJavaClassName( Class clazz )
-    {
-        if( clazz.isArray() )
-        {
-            return getJavaClassName(clazz.getComponentType()) + "[]";
-        }
-        return clazz.getName();
-    }
-
-    /**
-     * Returns the wrapper class for the given primitive type.
-     *
-     * @param primitiveType the primitive type
-     * @return the wrapper class
-     */
-    public static Class getWrapperClass( Class primitiveType )
-    {
-        return ( Class ) wrapperClassMap.get(primitiveType);
-    }
-
-    /**
-     * Returns the default value (null, zero, false) for a specified type.
-     * @param type the type
-     * @return the default value
-     */
-    public static Object getDefaultValue(Class type)
-    {
-        if( type.isPrimitive() )
-        {
-            return defaultValueMap.get(type);
-        }
-        else
-        {
-            return null;
-        }
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/exception/InvokerException.java b/src/main/java/org/apache/commons/proxy/exception/InvokerException.java
deleted file mode 100644
index 6e98bb4..0000000
--- a/src/main/java/org/apache/commons/proxy/exception/InvokerException.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.exception;
-
-/**
- * To be used by an {@link org.apache.commons.proxy.Invoker} when they encounter an error.
- *
- * @author James Carman
- * @since 1.0
- */
-public class InvokerException extends RuntimeException
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public InvokerException()
-    {
-    }
-
-    public InvokerException( String message )
-    {
-        super(message);
-    }
-
-    public InvokerException( Throwable cause )
-    {
-        super(cause);
-    }
-
-    public InvokerException( String message, Throwable cause )
-    {
-        super(message, cause);
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/exception/ObjectProviderException.java b/src/main/java/org/apache/commons/proxy/exception/ObjectProviderException.java
deleted file mode 100644
index b2c35d2..0000000
--- a/src/main/java/org/apache/commons/proxy/exception/ObjectProviderException.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.exception;
-
-/**
- * {@link org.apache.commons.proxy.ObjectProvider} implementations should throw this exception type to indicate that
- * there was a problem creating/finding the object.
- *
- * @author James Carman
- * @since 1.0
- */
-public class ObjectProviderException extends RuntimeException
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public ObjectProviderException()
-    {
-    }
-
-    public ObjectProviderException( String message )
-    {
-        super(message);
-    }
-
-    public ObjectProviderException( Throwable cause )
-    {
-        super(cause);
-    }
-
-    public ObjectProviderException( String message, Throwable cause )
-    {
-        super(message, cause);
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/exception/package.html b/src/main/java/org/apache/commons/proxy/exception/package.html
deleted file mode 100644
index 0216aab..0000000
--- a/src/main/java/org/apache/commons/proxy/exception/package.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-<html>
-<body>
-<p>
-    This package contains the exception classes used by the primary API.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/factory/cglib/package.html b/src/main/java/org/apache/commons/proxy/factory/cglib/package.html
deleted file mode 100644
index e0a406c..0000000
--- a/src/main/java/org/apache/commons/proxy/factory/cglib/package.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-<html>
-<body>
-<p>
-    This package contains the <a href="http://cglib.sourceforge.net/">CGLIB</a>-based
-    <a href="../../ProxyFactory.html">ProxyFactory</a> implementation.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/factory/javassist/package.html b/src/main/java/org/apache/commons/proxy/factory/javassist/package.html
deleted file mode 100644
index c8e64ee..0000000
--- a/src/main/java/org/apache/commons/proxy/factory/javassist/package.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<html>
-<body>
-<p>
-    This package contains the <a href="http://www.jboss.org/products/javassist">Javassist</a>-based
-    <a href="../../ProxyFactory.html">ProxyFactory</a> implementation.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/factory/util/MethodSignature.java b/src/main/java/org/apache/commons/proxy/factory/util/MethodSignature.java
deleted file mode 100644
index fabd415..0000000
--- a/src/main/java/org/apache/commons/proxy/factory/util/MethodSignature.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.util;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * A class for capturing the signature of a method (its name and parameter types).
- *
- * @author James Carman
- * @since 1.0
- */
-public class MethodSignature
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final String name;
-    private final List parameterTypes;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public MethodSignature( Method method )
-    {
-        this.name = method.getName();
-        this.parameterTypes = Arrays.asList(method.getParameterTypes());
-    }
-
-//**********************************************************************************************************************
-// Canonical Methods
-//**********************************************************************************************************************
-
-    public boolean equals( Object o )
-    {
-        if( this == o )
-        {
-            return true;
-        }
-        if( o == null || getClass() != o.getClass() )
-        {
-            return false;
-        }
-        final MethodSignature that = ( MethodSignature ) o;
-        if( !name.equals(that.name) )
-        {
-            return false;
-        }
-        if( !parameterTypes.equals(that.parameterTypes) )
-        {
-            return false;
-        }
-        return true;
-    }
-
-    public int hashCode()
-    {
-        int result;
-        result = name.hashCode();
-        result = 29 * result + parameterTypes.hashCode();
-        return result;
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/factory/util/package.html b/src/main/java/org/apache/commons/proxy/factory/util/package.html
deleted file mode 100644
index 0fea8e8..0000000
--- a/src/main/java/org/apache/commons/proxy/factory/util/package.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<html>
-<body>
-<p>
-    This package contains some classes useful for writing your own <a href="../../ProxyFactory.html">ProxyFactory</a>
-    implementation.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/ExecutorInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/ExecutorInterceptor.java
deleted file mode 100644
index 0608ea3..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/ExecutorInterceptor.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import EDU.oswego.cs.dl.util.concurrent.Executor;
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-
-/**
- * A method interceptor that uses an {@link Executor} to execute the method invocation.
- * <p/>
- * <b>Note</b>: Only <em>void</em> methods can be intercepted using this class!  Any attempts to intercept non-void
- * methods will result in an {@link IllegalArgumentException}.  If the proxy interfaces include non-void methods, try
- * using a {@link FilteredInterceptor} along with a
- * {@link org.apache.commons.proxy.interceptor.filter.ReturnTypeFilter} to wrap an instance of this class.
- * <p/>
- * <p>
- * <b>Dependencies</b>:
- * <ul>
- * <li>Concurrent API version 1.3.4 or greater</li>
- * </ul>
- * </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class ExecutorInterceptor implements Interceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Executor executor;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public ExecutorInterceptor( Executor executor )
-    {
-        this.executor = executor;
-    }
-
-//**********************************************************************************************************************
-// Interceptor Implementation
-//**********************************************************************************************************************
-
-
-    public Object intercept( final Invocation invocation ) throws Throwable
-    {
-        if( Void.TYPE.equals(invocation.getMethod().getReturnType()) )
-        {
-            // Special case for finalize() method (should not be run in a different thread)...
-            if( !( invocation.getMethod().getName().equals("finalize") &&
-                    invocation.getMethod().getParameterTypes().length == 0 ) )
-            {
-                executor.execute(new Runnable()
-                {
-                    public void run()
-                    {
-                        try
-                        {
-                            invocation.proceed();
-                        }
-                        catch( Throwable t )
-                        {
-                            // What to do here?  I can't convey the failure back to the caller.
-                        }
-                    }
-                });
-                return null;
-            }
-            else
-            {
-                return invocation.proceed();
-            }
-        }
-        else
-        {
-            throw new IllegalArgumentException("Only void methods can be executed in a different thread.");
-        }
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/FilteredInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/FilteredInterceptor.java
deleted file mode 100644
index 2479c69..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/FilteredInterceptor.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-
-/**
- * Decorates another <code>MethodInterceptor</code> by only calling it if the method is accepted by the supplied
- * <code>MethodFilter</code>.
- *
- * @author James Carman
- * @since 1.0
- */
-public class FilteredInterceptor implements Interceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Interceptor inner;
-    private final MethodFilter filter;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public FilteredInterceptor( Interceptor inner, MethodFilter filter )
-    {
-        this.inner = inner;
-        this.filter = filter;
-    }
-
-//**********************************************************************************************************************
-// Interceptor Implementation
-//**********************************************************************************************************************
-
-    public Object intercept( Invocation invocation ) throws Throwable
-    {
-        if( filter.accepts(invocation.getMethod()) )
-        {
-            return inner.intercept(invocation);
-        }
-        else
-        {
-            return invocation.proceed();
-        }
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/InterceptorChain.java b/src/main/java/org/apache/commons/proxy/interceptor/InterceptorChain.java
deleted file mode 100644
index b9437df..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/InterceptorChain.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.ProxyFactory;
-import org.apache.commons.proxy.ProxyUtils;
-
-/**
- * An <code>InterceptorChain</code> assists with creating proxies which go through a series of
- * {@link Interceptor interceptors}.
- * <p/>
- * <pre>
- *   MyServiceInterface serviceImpl = ...;
- *   ProxyFactory factory = ...;
- *   Interceptor[] interceptors = ...;
- *   InterceptorChain chain = new InterceptorChain(interceptors);
- *   ObjectProvider provider = chain.createProxyProvider(factory, serviceImpl);
- *   MyServiceInterface serviceProxy = ( MyServiceInterface )provider.getObject();
- *   serviceProxy.someServiceMethod(...); // This will go through the interceptors!
- * </pre>
- *
- * @author James Carman
- * @since 1.0
- */
-public class InterceptorChain
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Interceptor[] interceptors;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public InterceptorChain( Interceptor[] interceptors )
-    {
-        this.interceptors = interceptors;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Object createProxy( ProxyFactory proxyFactory, ClassLoader classLoader, Object terminus,
-                                Class[] proxyClasses )
-    {
-        Object currentTarget = terminus;
-        for( int i = interceptors.length - 1; i >= 0; --i )
-        {
-            currentTarget = proxyFactory
-                    .createInterceptorProxy(classLoader, currentTarget, interceptors[i], proxyClasses);
-        }
-        return currentTarget;
-    }
-
-    /**
-     * Creates an {@link ObjectProvider} which will return a proxy that sends method invocations through this
-     * chain of interceptors and ultimately arrive at the supplied terminus object.  The proxy will support all
-     * interfaces implemented by the terminus object.  The thread context classloader will be used to generate the
-     * proxy class.
-     *
-     * @param proxyFactory the {@link ProxyFactory} to use to create the proxy
-     * @param terminus     the terminus
-     * @return an {@link ObjectProvider} which will return a proxy that sends method invocations through this
-     *         chain of interceptors and ultimately arrive at the supplied terminus object
-     */
-    public ObjectProvider createProxyProvider( ProxyFactory proxyFactory, Object terminus )
-    {
-        return createProxyProvider(proxyFactory, terminus, null);
-    }
-
-    /**
-     * Creates an {@link ObjectProvider} which will return a proxy that sends method invocations through this
-     * chain of interceptors and ultimately arrive at the supplied terminus object.  The proxy will support only
-     * the specified interfaces/classes.  The thread context classloader will be used to generate the
-     * proxy class.
-     *
-     * @param proxyFactory the {@link ProxyFactory} to use to create the proxy
-     * @param terminus     the terminus
-     * @param proxyClasses the interfaces to support
-     * @return an {@link ObjectProvider} which will return a proxy that sends method invocations through this
-     *         chain of interceptors and ultimately arrive at the supplied terminus object
-     */
-    public ObjectProvider createProxyProvider( ProxyFactory proxyFactory, Object terminus, Class[] proxyClasses )
-    {
-        return createProxyProvider(proxyFactory, Thread.currentThread().getContextClassLoader(), terminus,
-                proxyClasses);
-    }
-
-    /**
-     * Creates an {@link ObjectProvider} which will return a proxy that sends method invocations through this
-     * chain of interceptors and ultimately arrive at the supplied terminus object.  The proxy will support only
-     * the specified interfaces/classes.  The specified classloader will be used to generate the
-     * proxy class.
-     *
-     * @param proxyFactory the {@link ProxyFactory} to use to create the proxy
-     * @param classLoader  the classloader to be used to generate the proxy class
-     * @param terminus     the terminus
-     * @param proxyClasses the interfaces to support
-     * @return an {@link ObjectProvider} which will return a proxy that sends method invocations through this
-     *         chain of interceptors and ultimately arrive at the supplied terminus object
-     */
-    public ObjectProvider createProxyProvider( ProxyFactory proxyFactory, ClassLoader classLoader, Object terminus,
-                                               Class[] proxyClasses )
-    {
-        if( proxyClasses == null || proxyClasses.length == 0 )
-        {
-            proxyClasses = ProxyUtils.getAllInterfaces(terminus.getClass());
-        }
-        return new ProxyObjectProvider(proxyFactory, classLoader, terminus, proxyClasses);
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private class ProxyObjectProvider implements ObjectProvider
-    {
-        private final ClassLoader classLoader;
-        private final Class[] proxyClasses;
-        private final Object terminus;
-        private final ProxyFactory proxyFactory;
-
-        public ProxyObjectProvider( ProxyFactory proxyFactory, ClassLoader classLoader, Object terminus,
-                                    Class[] proxyClasses )
-        {
-            this.classLoader = classLoader;
-            this.proxyClasses = proxyClasses;
-            this.terminus = terminus;
-            this.proxyFactory = proxyFactory;
-        }
-
-        public Object getObject()
-        {
-            return createProxy(proxyFactory, classLoader, terminus, proxyClasses);
-        }
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/LoggingInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/LoggingInterceptor.java
deleted file mode 100644
index c448f96..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/LoggingInterceptor.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-import org.apache.commons.proxy.ProxyUtils;
-
-/**
- * An interceptor which logs each method invocation.
- * <b>Note</b>: The implementation of this class was borrowed from
- * HiveMind's logging interceptor.
- * <p/>
- * <p>
- * <b>Dependencies</b>:
- * <ul>
- * <li>Apache Commons Logging version 1.0.4 or greater</li>
- * </ul>
- * </p>
- *
- * @author James Carman
- * @since 1.0
- * @deprecated please use {@link org.apache.commons.proxy.interceptor.logging.CommonsLoggingInterceptor} instead
- */
-public class LoggingInterceptor implements Interceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static final int BUFFER_SIZE = 100;
-    private Log log;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public LoggingInterceptor( Log log )
-    {
-        this.log = log;
-    }
-
-//**********************************************************************************************************************
-// Interceptor Implementation
-//**********************************************************************************************************************
-
-    public Object intercept( Invocation invocation ) throws Throwable
-    {
-        if( log.isDebugEnabled() )
-        {
-            final String methodName = invocation.getMethod().getName();
-            entry(methodName, invocation.getArguments());
-            try
-            {
-                Object result = invocation.proceed();
-                if( Void.TYPE.equals(invocation.getMethod().getReturnType()) )
-                {
-                    voidExit(methodName);
-                }
-                else
-                {
-                    exit(methodName, result);
-                }
-                return result;
-            }
-            catch( Throwable t )
-            {
-                exception(methodName, t);
-                throw t;
-            }
-        }
-        else
-        {
-            return invocation.proceed();
-        }
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private void convert( StringBuffer buffer, Object input )
-    {
-        if( input == null )
-        {
-            buffer.append("<null>");
-            return;
-        }
-
-        // Primitive types, and non-object arrays
-        // use toString().  Less than ideal for int[], etc., but
-        // that's a lot of work for a rare case.
-        if( !( input instanceof Object[] ) )
-        {
-            buffer.append(input.toString());
-            return;
-        }
-        buffer.append("(");
-        buffer.append(ProxyUtils.getJavaClassName(input.getClass()));
-        buffer.append("){");
-        Object[] array = ( Object[] ) input;
-        int count = array.length;
-        for( int i = 0; i < count; i++ )
-        {
-            if( i > 0 )
-            {
-                buffer.append(", ");
-            }
-
-            // We use convert() again, because it could be a multi-dimensional array
-            // (god help us) where each element must be converted.
-            convert(buffer, array[i]);
-        }
-        buffer.append("}");
-    }
-
-    private void entry( String methodName, Object[] args )
-    {
-        StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
-        buffer.append("BEGIN ");
-        buffer.append(methodName);
-        buffer.append("(");
-        int count = args.length;
-        for( int i = 0; i < count; i++ )
-        {
-            Object arg = args[i];
-            if( i > 0 )
-            {
-                buffer.append(", ");
-            }
-            convert(buffer, arg);
-        }
-        buffer.append(")");
-        log.debug(buffer.toString());
-    }
-
-    private void exception( String methodName, Throwable t )
-    {
-        StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
-        buffer.append("EXCEPTION ");
-        buffer.append(methodName);
-        buffer.append("() -- ");
-        buffer.append(t.getClass().getName());
-        log.debug(buffer.toString(), t);
-    }
-
-    private void exit( String methodName, Object result )
-    {
-        StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
-        buffer.append("END ");
-        buffer.append(methodName);
-        buffer.append("() [");
-        convert(buffer, result);
-        buffer.append("]");
-        log.debug(buffer.toString());
-    }
-
-    private void voidExit( String methodName )
-    {
-        StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
-        buffer.append("END ");
-        buffer.append(methodName);
-        buffer.append("()");
-        log.debug(buffer.toString());
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/MethodFilter.java b/src/main/java/org/apache/commons/proxy/interceptor/MethodFilter.java
deleted file mode 100644
index db0c80d..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/MethodFilter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import java.lang.reflect.Method;
-import java.io.Serializable;
-
-/**
- * A <code>MethodFilter</code> determines whether or not a method is accepted.
- *
- * @author James Carman
- * @since 1.0
- */
-public interface MethodFilter extends Serializable
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns whether or not this filter accepts this method.
-     *
-     * @param method the method
-     * @return whether or not this filter accepts this method
-     */
-    public boolean accepts( Method method );
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/MethodInterceptorAdapter.java b/src/main/java/org/apache/commons/proxy/interceptor/MethodInterceptorAdapter.java
deleted file mode 100644
index 09ad3ba..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/MethodInterceptorAdapter.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.aopalliance.intercept.MethodInterceptor;
-import org.aopalliance.intercept.MethodInvocation;
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Method;
-
-/**
- * An adapter class to adapt AOP Alliance's {@link MethodInterceptor} interface to Commons Proxy's
- * {@link Interceptor} interface.
- * <p/>
- * <p>
- * <b>Dependencies</b>:
- * <ul>
- * <li>AOP Alliance API version 1.0 or greater</li>
- * </ul>
- * </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class MethodInterceptorAdapter implements Interceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final MethodInterceptor methodInterceptor;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public MethodInterceptorAdapter( MethodInterceptor methodInterceptor )
-    {
-        this.methodInterceptor = methodInterceptor;
-    }
-
-//**********************************************************************************************************************
-// Interceptor Implementation
-//**********************************************************************************************************************
-
-
-    public Object intercept( Invocation invocation ) throws Throwable
-    {
-        return methodInterceptor.invoke(new MethodInvocationAdapter(invocation));
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private static class MethodInvocationAdapter implements MethodInvocation
-    {
-        private final Invocation invocation;
-
-        public MethodInvocationAdapter( Invocation invocation )
-        {
-            this.invocation = invocation;
-        }
-
-        public Method getMethod()
-        {
-            return invocation.getMethod();
-        }
-
-        public Object[] getArguments()
-        {
-            return invocation.getArguments();
-        }
-
-        public Object proceed() throws Throwable
-        {
-            return invocation.proceed();
-        }
-
-        public Object getThis()
-        {
-            return invocation.getProxy();
-        }
-
-        public AccessibleObject getStaticPart()
-        {
-            return invocation.getMethod();
-        }
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/SerializingInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/SerializingInterceptor.java
deleted file mode 100644
index 8dc3025..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/SerializingInterceptor.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-
-/**
- * An interceptor which makes a serialized copy of all parameters and return values.  This
- * is useful when testing remote services to ensure that all parameter/return types
- * are in fact serializable/deserializable.
- *
- * @since 1.0
- */
-public class SerializingInterceptor implements Interceptor, Serializable
-{
-//**********************************************************************************************************************
-// Interceptor Implementation
-//**********************************************************************************************************************
-
-    public Object intercept( Invocation invocation ) throws Throwable
-    {
-        Object[] arguments = invocation.getArguments();
-        for( int i = 0; i < arguments.length; i++ )
-        {
-            arguments[i] = serializedCopy(arguments[i]);
-        }
-        return serializedCopy(invocation.proceed());
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Object serializedCopy( Object original )
-    {
-        try
-        {
-            final ByteArrayOutputStream bout = new ByteArrayOutputStream();
-            final ObjectOutputStream oout = new ObjectOutputStream(bout);
-            oout.writeObject(original);
-            oout.close();
-            bout.close();
-            final ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
-            final ObjectInputStream oin = new ObjectInputStream(bin);
-            final Object copy = oin.readObject();
-            oin.close();
-            bin.close();
-            return copy;
-        }
-        catch( IOException e )
-        {
-            throw new RuntimeException("Unable to make serialized copy of " +
-                    original.getClass().getName() + " object.", e);
-        }
-        catch( ClassNotFoundException e )
-        {
-            throw new RuntimeException("Unable to make serialized copy of " +
-                    original.getClass().getName() + " object.", e);
-        }
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/filter/PatternFilter.java b/src/main/java/org/apache/commons/proxy/interceptor/filter/PatternFilter.java
deleted file mode 100644
index a926b0b..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/filter/PatternFilter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.filter;
-
-import org.apache.commons.proxy.interceptor.MethodFilter;
-
-import java.lang.reflect.Method;
-
-/**
- * A method filter implementation that returns true if the method's name matches a supplied regular expression (JDK
- * regex) pattern string.
- *
- * @author James Carman
- * @since 1.0
- */
-public class PatternFilter implements MethodFilter
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    public static String GETTER_SETTER_PATTERN = "get\\w+|set\\w+";
-    private final String pattern;
-
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    /**
-     * Returns a {@link MethodFilter} which accepts only "getters" and "setters."
-     *
-     * @return a {@link MethodFilter} which accepts only "getters" and "setters."
-     */
-    public static MethodFilter getterSetterFilter()
-    {
-        return new PatternFilter(GETTER_SETTER_PATTERN);
-    }
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public PatternFilter( String pattern )
-    {
-        this.pattern = pattern;
-    }
-
-//**********************************************************************************************************************
-// MethodFilter Implementation
-//**********************************************************************************************************************
-
-    public boolean accepts( Method method )
-    {
-        return method.getName().matches(pattern);
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/filter/ReturnTypeFilter.java b/src/main/java/org/apache/commons/proxy/interceptor/filter/ReturnTypeFilter.java
deleted file mode 100644
index bfae910..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/filter/ReturnTypeFilter.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.filter;
-
-import org.apache.commons.proxy.interceptor.MethodFilter;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Filters methods based on their return type.
- *
- * @author James Carman
- * @since 1.0
- */
-public class ReturnTypeFilter implements MethodFilter
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Set validReturnTypes = new HashSet();
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public ReturnTypeFilter( Class[] validReturnTypes )
-    {
-        this(Arrays.asList(validReturnTypes));
-    }
-
-    public ReturnTypeFilter( Collection validReturnTypes )
-    {
-        this.validReturnTypes.addAll(validReturnTypes);
-    }
-
-//**********************************************************************************************************************
-// MethodFilter Implementation
-//**********************************************************************************************************************
-
-
-    public boolean accepts( Method method )
-    {
-        return validReturnTypes.contains(method.getReturnType());
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/filter/SimpleFilter.java b/src/main/java/org/apache/commons/proxy/interceptor/filter/SimpleFilter.java
deleted file mode 100644
index 4f9f08a..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/filter/SimpleFilter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.filter;
-
-import org.apache.commons.proxy.interceptor.MethodFilter;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A simple method filter implementation that merely returns true if the method's name is in a set of accepted names.
- *
- * @author James Carman
- * @since 1.0
- */
-public class SimpleFilter implements MethodFilter
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Set methodNames;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    /**
-     * Creates a simple filter that accepts no methods.
-     */
-    public SimpleFilter()
-    {
-        this.methodNames = new HashSet();
-    }
-
-    /**
-     * Creates a simple filter that accepts methods matching the supplied names.
-     *
-     * @param methodNames the names
-     */
-    public SimpleFilter( String[] methodNames )
-    {
-        this.methodNames = new HashSet(Arrays.asList(methodNames));
-    }
-
-//**********************************************************************************************************************
-// MethodFilter Implementation
-//**********************************************************************************************************************
-
-    public boolean accepts( Method method )
-    {
-        return methodNames.contains(method.getName());
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/filter/package.html b/src/main/java/org/apache/commons/proxy/interceptor/filter/package.html
deleted file mode 100644
index 97e6fdc..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/filter/package.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<html>
-<body>
-<p>
-    This package contains some useful <a href="../MethodFilter.html">MethodFilter</a> implementations.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/logging/AbstractLoggingInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/logging/AbstractLoggingInterceptor.java
deleted file mode 100644
index 72fd527..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/logging/AbstractLoggingInterceptor.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.logging;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-import org.apache.commons.proxy.ProxyUtils;
-
-import java.lang.reflect.Method;
-
-/**
- * @author James Carman
- * @since 1.1
- */
-public abstract class AbstractLoggingInterceptor implements Interceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static final int BUFFER_SIZE = 100;
-
-//**********************************************************************************************************************
-// Abstract Methods
-//**********************************************************************************************************************
-
-    protected abstract boolean isLoggingEnabled();
-
-    protected abstract void logMessage( String message );
-
-    protected abstract void logMessage( String message, Throwable t );
-
-//**********************************************************************************************************************
-// Interceptor Implementation
-//**********************************************************************************************************************
-
-    public Object intercept( Invocation invocation ) throws Throwable
-    {
-        if( isLoggingEnabled() )
-        {
-            final Method method = invocation.getMethod();
-            entering(method, invocation.getArguments());
-            try
-            {
-                Object result = invocation.proceed();
-                exiting(method, result);
-                return result;
-            }
-            catch( Throwable t )
-            {
-                throwing(method, t);
-                throw t;
-            }
-        }
-        else
-        {
-            return invocation.proceed();
-        }
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    protected void entering( Method method, Object[] args )
-    {
-        StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
-        buffer.append("BEGIN ");
-        buffer.append(method.getName());
-        buffer.append("(");
-        int count = args.length;
-        for( int i = 0; i < count; i++ )
-        {
-            Object arg = args[i];
-            if( i > 0 )
-            {
-                buffer.append(", ");
-            }
-            convert(buffer, arg);
-        }
-        buffer.append(")");
-        logMessage(buffer.toString());
-    }
-
-    protected void convert( StringBuffer buffer, Object input )
-    {
-        if( input == null )
-        {
-            buffer.append("<null>");
-            return;
-        }
-
-        // Primitive types, and non-object arrays
-        // use toString().  Less than ideal for int[], etc., but
-        // that's a lot of work for a rare case.
-        if( !( input instanceof Object[] ) )
-        {
-            buffer.append(input.toString());
-            return;
-        }
-        buffer.append("(");
-        buffer.append(ProxyUtils.getJavaClassName(input.getClass()));
-        buffer.append("){");
-        Object[] array = ( Object[] ) input;
-        int count = array.length;
-        for( int i = 0; i < count; i++ )
-        {
-            if( i > 0 )
-            {
-                buffer.append(", ");
-            }
-
-            // We use convert() again, because it could be a multi-dimensional array
-            // (god help us) where each element must be converted.
-            convert(buffer, array[i]);
-        }
-        buffer.append("}");
-    }
-
-    protected void exiting( Method method, Object result )
-    {
-        StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
-        buffer.append("END ");
-        buffer.append(method.getName());
-        buffer.append("()");
-        if( !Void.TYPE.equals(method.getReturnType()) )
-        {
-            buffer.append(" [");
-            convert(buffer, result);
-            buffer.append("]");
-        }
-        logMessage(buffer.toString());
-    }
-
-    protected void throwing( Method method, Throwable t )
-    {
-        StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
-        buffer.append("EXCEPTION ");
-        buffer.append(method);
-        buffer.append("() -- ");
-        buffer.append(t.getClass().getName());
-        logMessage(buffer.toString(), t);
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/logging/CommonsLoggingInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/logging/CommonsLoggingInterceptor.java
deleted file mode 100644
index 4a6f8d4..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/logging/CommonsLoggingInterceptor.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.logging;
-
-import org.apache.commons.logging.LogFactory;
-import org.apache.commons.logging.Log;
-
-/**
- * An interceptor which logs method invocations using an <a href="http://commons.apache.org/logging">Apache Commons
- * Logging</a> {@link Log} object at the "trace" level.
- * 
- * @author James Carman
- * @since 1.1
- */
-public class CommonsLoggingInterceptor extends AbstractLoggingInterceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final String logName;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public CommonsLoggingInterceptor( String logName )
-    {
-        this.logName = logName;
-    }
-
-    public CommonsLoggingInterceptor(Class clazz)
-    {
-        this(clazz.getName());
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Log getLog()
-    {
-        return LogFactory.getLog(logName);
-    }
-
-    protected boolean isLoggingEnabled()
-    {
-        return getLog().isTraceEnabled();
-    }
-
-    protected void logMessage( String message )
-    {
-        getLog().trace(message);
-    }
-
-    protected void logMessage( String message, Throwable t )
-    {
-        getLog().trace(message, t);
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/logging/JdkLoggingInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/logging/JdkLoggingInterceptor.java
deleted file mode 100644
index a36ef27..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/logging/JdkLoggingInterceptor.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.logging;
-
-import java.lang.reflect.Method;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * An interceptor which logs method invocations using a {@link Logger JDK logger} using the "finer" logging level.
- * 
- * @author James Carman
- * @since 1.1
- */
-public class JdkLoggingInterceptor extends AbstractLoggingInterceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final String loggerName;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public JdkLoggingInterceptor( Class clazz )
-    {
-        this(clazz.getName());
-    }
-
-    public JdkLoggingInterceptor( String loggerName )
-    {
-        this.loggerName = loggerName;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Logger getLogger()
-    {
-        return Logger.getLogger(loggerName);
-    }
-
-    protected void entering( Method method, Object[] args )
-    {
-        getLogger().entering(method.getDeclaringClass().getName(), method.getName(), args);
-    }
-
-    protected void exiting( Method method, Object result )
-    {
-        getLogger().exiting(method.getDeclaringClass().getName(), method.getName(), result);
-    }
-
-    protected boolean isLoggingEnabled()
-    {
-        return getLogger().isLoggable(Level.FINER);
-    }
-
-    protected void logMessage( String message )
-    {
-        // Do nothing!
-    }
-
-    protected void logMessage( String message, Throwable t )
-    {
-        // Do nothing!
-    }
-
-    protected void throwing( Method method, Throwable t )
-    {
-        getLogger().throwing(method.getDeclaringClass().getName(), method.getName(), t);
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/logging/Slf4jInterceptor.java b/src/main/java/org/apache/commons/proxy/interceptor/logging/Slf4jInterceptor.java
deleted file mode 100644
index 2510f1d..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/logging/Slf4jInterceptor.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.logging;
-
-import org.slf4j.LoggerFactory;
-import org.slf4j.Logger;
-
-/**
- * An interceptor which logs method invocations using an <a href="http://www.slf4j.org/">SLF4J</a> {@link Logger} at the
- * "trace" logging level.
- * 
- * @author James Carman
- * @since 1.1
- */
-public class Slf4jInterceptor extends AbstractLoggingInterceptor
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final String loggerName;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public Slf4jInterceptor( Class clazz )
-    {
-        this(clazz.getName());
-    }
-
-    public Slf4jInterceptor( String loggerName )
-    {
-        this.loggerName = loggerName;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Logger getLogger()
-    {
-        return LoggerFactory.getLogger(loggerName);
-    }
-
-    protected boolean isLoggingEnabled()
-    {
-        return getLogger().isTraceEnabled();
-    }
-
-    protected void logMessage( String message )
-    {
-        getLogger().debug(message);
-    }
-
-    protected void logMessage( String message, Throwable t )
-    {
-        getLogger().debug(message, t);
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/interceptor/package.html b/src/main/java/org/apache/commons/proxy/interceptor/package.html
deleted file mode 100644
index 2d4df26..0000000
--- a/src/main/java/org/apache/commons/proxy/interceptor/package.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<html>
-<body>
-<p>
-    This package contains some useful <a href="../Interceptor.html">Interceptor</a> implementations.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/invoker/ChainInvoker.java b/src/main/java/org/apache/commons/proxy/invoker/ChainInvoker.java
deleted file mode 100644
index 25f92e1..0000000
--- a/src/main/java/org/apache/commons/proxy/invoker/ChainInvoker.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.Invoker;
-import org.apache.commons.proxy.ProxyUtils;
-
-import java.lang.reflect.Method;
-
-/**
- * A chain invoker will invoke the method on each object in the chain until one of them
- * returns a non-default value
- *
- * @author James Carman
- * @since 1.1
- */
-public class ChainInvoker implements Invoker
-{
-    private final Object[] targets;
-
-    public ChainInvoker(Object[] targets)
-    {
-        this.targets = targets;
-    }
-
-    public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable
-    {
-        for (int i = 0; i < targets.length; i++)
-        {
-            Object target = targets[i];
-            Object value = method.invoke(target, arguments);
-            if (value != null && !value.equals(ProxyUtils.getDefaultValue(method.getReturnType())))
-            {
-                return value;
-            }
-        }
-        return ProxyUtils.getDefaultValue(method.getReturnType());
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/invoker/XmlRpcInvoker.java b/src/main/java/org/apache/commons/proxy/invoker/XmlRpcInvoker.java
deleted file mode 100644
index a9c8f2d..0000000
--- a/src/main/java/org/apache/commons/proxy/invoker/XmlRpcInvoker.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.Invoker;
-import org.apache.commons.proxy.exception.InvokerException;
-import org.apache.xmlrpc.XmlRpcException;
-import org.apache.xmlrpc.XmlRpcHandler;
-
-import java.lang.reflect.Method;
-import java.util.Vector;
-
-/**
- * Uses <a href="http://ws.apache.org/xmlrpc/">Apache XML-RPC</a> to invoke methods on an XML-RPC service.
- * <p/>
- * <p>
- * <b>Dependencies</b>:
- * <ul>
- * <li>Apache XML-RPC version 2.0 or greater</li>
- * </ul>
- * </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class XmlRpcInvoker implements Invoker
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final XmlRpcHandler handler;
-    private final String handlerName;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public XmlRpcInvoker( XmlRpcHandler handler, String handlerName )
-    {
-        this.handler = handler;
-        this.handlerName = handlerName;
-    }
-
-//**********************************************************************************************************************
-// Invoker Implementation
-//**********************************************************************************************************************
-
-
-    public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
-    {
-        final Object returnValue = handler.execute(handlerName + "." + method.getName(), toArgumentVector(args));
-        if( returnValue instanceof XmlRpcException )
-        {
-            throw new InvokerException("Unable to execute XML-RPC call.", ( XmlRpcException ) returnValue);
-        }
-        return returnValue;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Vector toArgumentVector( Object[] args )
-    {
-        final Vector v = new Vector();
-        for( int i = 0; i < args.length; i++ )
-        {
-            Object arg = args[i];
-            v.addElement(arg);
-        }
-        return v;
-    }
-}
diff --git a/src/main/java/org/apache/commons/proxy/invoker/package.html b/src/main/java/org/apache/commons/proxy/invoker/package.html
deleted file mode 100644
index 63de2f5..0000000
--- a/src/main/java/org/apache/commons/proxy/invoker/package.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<html>
-<body>
-<p>
-    This package contains some useful <a href="../Invoker.html">Invoker</a> implementations.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/package.html b/src/main/java/org/apache/commons/proxy/package.html
deleted file mode 100644
index 98a831a..0000000
--- a/src/main/java/org/apache/commons/proxy/package.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<body>
-<p>
-    This package contains the primary API.
-</p>
-</body>
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/proxy/provider/ConstantProvider.java b/src/main/java/org/apache/commons/proxy/provider/ConstantProvider.java
deleted file mode 100644
index 8645848..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/ConstantProvider.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider;
-
-import org.apache.commons.proxy.ObjectProvider;
-
-import java.io.Serializable;
-
-/**
- * Always returns the same object.
- *
- * @author James Carman
- * @since 1.0
- */
-public class ConstantProvider implements ObjectProvider, Serializable
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final Object constant;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public ConstantProvider( Object constant )
-    {
-        this.constant = constant;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        return constant;
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/provider/ProviderDecorator.java b/src/main/java/org/apache/commons/proxy/provider/ProviderDecorator.java
deleted file mode 100644
index 251ece8..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/ProviderDecorator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider;
-
-import org.apache.commons.proxy.ObjectProvider;
-
-/**
- * Returns the result of the inner {@link ObjectProvider provider}.  Subclasses can override the {@link #getObject()}
- * method and decorate what comes back from the inner provider in some way (by {@link SingletonProvider caching it} for
- * example).
- *
- * @author James Carman
- * @since 1.0
- */
-public class ProviderDecorator implements ObjectProvider
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    protected ObjectProvider inner;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public ProviderDecorator( ObjectProvider inner )
-    {
-        this.inner = inner;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        return inner.getObject();
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/provider/package.html b/src/main/java/org/apache/commons/proxy/provider/package.html
deleted file mode 100644
index c5a0977..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/package.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<html>
-<body>
-<p>
-    This package contains some general-use <a href="../ObjectProvider.html">ObjectProvider</a> implementations.
-</p>
-</body>
-</html>
diff --git a/src/main/java/org/apache/commons/proxy/provider/remoting/BurlapProvider.java b/src/main/java/org/apache/commons/proxy/provider/remoting/BurlapProvider.java
deleted file mode 100644
index 6aac5b8..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/remoting/BurlapProvider.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider.remoting;
-
-import com.caucho.burlap.client.BurlapProxyFactory;
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import java.io.Serializable;
-import java.net.MalformedURLException;
-
-/**
- * Provides a burlap service object.
- * <p/>
- * <p>
- * <b>Dependencies</b>:
- * <ul>
- * <li>Burlap version 2.1.7 or greater</li>
- * </ul>
- * </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class BurlapProvider implements ObjectProvider, Serializable
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private Class serviceInterface;
-    private String url;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public BurlapProvider()
-    {
-    }
-
-    public BurlapProvider( Class serviceInterface, String url )
-    {
-        this.serviceInterface = serviceInterface;
-        this.url = url;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        try
-        {
-            return new BurlapProxyFactory().create(serviceInterface, url);
-        }
-        catch( MalformedURLException e )
-        {
-            throw new ObjectProviderException("Invalid url given.", e);
-        }
-    }
-
-//**********************************************************************************************************************
-// Getter/Setter Methods
-//**********************************************************************************************************************
-
-    public void setServiceInterface( Class serviceInterface )
-    {
-        this.serviceInterface = serviceInterface;
-    }
-
-    public void setUrl( String url )
-    {
-        this.url = url;
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/provider/remoting/HessianProvider.java b/src/main/java/org/apache/commons/proxy/provider/remoting/HessianProvider.java
deleted file mode 100644
index aacf244..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/remoting/HessianProvider.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider.remoting;
-
-import com.caucho.hessian.client.HessianProxyFactory;
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import java.io.Serializable;
-import java.net.MalformedURLException;
-
-/**
- * Provides a hessian service object.
- * <p/>
- * <p>
- * <b>Dependencies</b>:
- * <ul>
- * <li>Hessian version 3.0.1 or greater</li>
- * </ul>
- * </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class HessianProvider implements ObjectProvider, Serializable
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private Class serviceInterface;
-    private String url;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public HessianProvider()
-    {
-    }
-
-    public HessianProvider( Class serviceInterface, String url )
-    {
-        this.serviceInterface = serviceInterface;
-        this.url = url;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        try
-        {
-            return new HessianProxyFactory().create(serviceInterface, url);
-        }
-        catch( MalformedURLException e )
-        {
-            throw new ObjectProviderException("Invalid url given.", e);
-        }
-    }
-
-//**********************************************************************************************************************
-// Getter/Setter Methods
-//**********************************************************************************************************************
-
-    public void setServiceInterface( Class serviceInterface )
-    {
-        this.serviceInterface = serviceInterface;
-    }
-
-    public void setUrl( String url )
-    {
-        this.url = url;
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/provider/remoting/JaxRpcProvider.java b/src/main/java/org/apache/commons/proxy/provider/remoting/JaxRpcProvider.java
deleted file mode 100644
index 15ce7b1..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/remoting/JaxRpcProvider.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider.remoting;
-
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import javax.xml.namespace.QName;
-import javax.xml.rpc.Service;
-import javax.xml.rpc.ServiceException;
-import javax.xml.rpc.ServiceFactory;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * Returns a proxy for a JAX-RPC-based service.
- * <p/>
- * <p>
- * <b>Dependencies</b>:
- * <ul>
- * <li>A JAX-RPC implementation</li>
- * </ul>
- * </p>
- *
- * @author James Carman
- * @since 1.0
- */
-public class JaxRpcProvider implements ObjectProvider
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private Class serviceInterface;
-    private String wsdlUrl;
-    private String serviceNamespaceUri;
-    private String serviceLocalPart;
-    private String servicePrefix;
-    private String portNamespaceUri;
-    private String portLocalPart;
-    private String portPrefix;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public JaxRpcProvider()
-    {
-    }
-
-    public JaxRpcProvider( Class serviceInterface )
-    {
-        this.serviceInterface = serviceInterface;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        try
-        {
-            final Service service = ( wsdlUrl == null ?
-                    ServiceFactory.newInstance().createService(getServiceQName()) : ServiceFactory
-                    .newInstance().createService(new URL(wsdlUrl), getServiceQName()) );
-            final QName portQName = getPortQName();
-            return portQName == null ? service.getPort(serviceInterface) :
-                    service.getPort(portQName, serviceInterface);
-        }
-        catch( ServiceException e )
-        {
-            throw new ObjectProviderException("Unable to create JAX-RPC service proxy.", e);
-        }
-        catch( MalformedURLException e )
-        {
-            throw new ObjectProviderException("Invalid URL given.", e);
-        }
-    }
-
-//**********************************************************************************************************************
-// Getter/Setter Methods
-//**********************************************************************************************************************
-
-    public void setPortLocalPart( String portLocalPart )
-    {
-        this.portLocalPart = portLocalPart;
-    }
-
-    public void setPortNamespaceUri( String portNamespaceUri )
-    {
-        this.portNamespaceUri = portNamespaceUri;
-    }
-
-    public void setPortPrefix( String portPrefix )
-    {
-        this.portPrefix = portPrefix;
-    }
-
-    public void setServiceInterface( Class serviceInterface )
-    {
-        this.serviceInterface = serviceInterface;
-    }
-
-    public void setServiceLocalPart( String serviceLocalPart )
-    {
-        this.serviceLocalPart = serviceLocalPart;
-    }
-
-    public void setServiceNamespaceUri( String serviceNamespaceUri )
-    {
-        this.serviceNamespaceUri = serviceNamespaceUri;
-    }
-
-    public void setServicePrefix( String servicePrefix )
-    {
-        this.servicePrefix = servicePrefix;
-    }
-
-    public void setWsdlUrl( String wsdlUrl )
-    {
-        this.wsdlUrl = wsdlUrl;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private QName getPortQName()
-    {
-        return getQName(portNamespaceUri, portLocalPart, portPrefix);
-    }
-
-    private QName getQName( String namespaceUri, String localPart, String prefix )
-    {
-        if( namespaceUri != null && localPart != null && prefix != null )
-        {
-            return new QName(namespaceUri, localPart, prefix);
-        }
-        else if( namespaceUri != null && localPart != null )
-        {
-            return new QName(namespaceUri, localPart);
-        }
-        else if( localPart != null )
-        {
-            return new QName(localPart);
-        }
-        return null;
-    }
-
-    private QName getServiceQName()
-    {
-        return getQName(serviceNamespaceUri, serviceLocalPart, servicePrefix);
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/provider/remoting/RmiProvider.java b/src/main/java/org/apache/commons/proxy/provider/remoting/RmiProvider.java
deleted file mode 100644
index 1dd7edf..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/remoting/RmiProvider.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider.remoting;
-
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import java.rmi.NotBoundException;
-import java.rmi.RemoteException;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
-import java.rmi.server.RMIClientSocketFactory;
-
-/**
- * Provides an object by looking it up in an RMI registry.
- *
- * @author James Carman
- * @since 1.0
- */
-public class RmiProvider implements ObjectProvider
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private String host = "localhost";
-    private int port = Registry.REGISTRY_PORT;
-    private RMIClientSocketFactory clientSocketFactory;
-    private String name;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public RmiProvider()
-    {
-    }
-
-    public RmiProvider( String name )
-    {
-        setName(name);
-    }
-
-    public RmiProvider( String host, String name )
-    {
-        setHost(host);
-        setName(name);
-    }
-
-    public RmiProvider( String host, int port, String name )
-    {
-        setHost(host);
-        setName(name);
-        setPort(port);
-    }
-
-    public RmiProvider( String host, int port, RMIClientSocketFactory clientSocketFactory, String name )
-    {
-        setHost(host);
-        setPort(port);
-        setClientSocketFactory(clientSocketFactory);
-        setName(name);
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        Registry reg = null;
-        try
-        {
-            reg = getRegistry();
-            return reg.lookup(name);
-        }
-        catch( NotBoundException e )
-        {
-            throw new ObjectProviderException("Name " + name + " not found in registry at " + host + ":" + port + ".",
-                    e);
-        }
-        catch( RemoteException e )
-        {
-            throw new ObjectProviderException(
-                    "Unable to lookup service named " + name + " in registry at " + host + ":" + port + ".", e);
-        }
-    }
-
-//**********************************************************************************************************************
-// Getter/Setter Methods
-//**********************************************************************************************************************
-
-    public void setClientSocketFactory( RMIClientSocketFactory clientSocketFactory )
-    {
-        this.clientSocketFactory = clientSocketFactory;
-    }
-
-    public void setHost( String host )
-    {
-        this.host = host;
-    }
-
-    public void setName( String name )
-    {
-        this.name = name;
-    }
-
-    public void setPort( int port )
-    {
-        this.port = port;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private Registry getRegistry()
-    {
-        try
-        {
-            if( clientSocketFactory != null )
-            {
-                return LocateRegistry.getRegistry(host, port, clientSocketFactory);
-            }
-            else
-            {
-                return LocateRegistry.getRegistry(host, port);
-            }
-        }
-        catch( RemoteException e )
-        {
-            throw new ObjectProviderException("Unable to locate registry at " + host + ":" + port + ".", e);
-        }
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/provider/remoting/SessionBeanProvider.java b/src/main/java/org/apache/commons/proxy/provider/remoting/SessionBeanProvider.java
deleted file mode 100644
index 4499f0a..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/remoting/SessionBeanProvider.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider.remoting;
-
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.ProxyUtils;
-import org.apache.commons.proxy.exception.ObjectProviderException;
-
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.rmi.PortableRemoteObject;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Properties;
-
-/**
- * Provides a reference to a session bean by looking up the home object and calling (via reflection) the no-argument
- * create() method.  This will work for both local and remote session beans.
- *
- * @author James Carman
- * @since 1.0
- */
-public class SessionBeanProvider implements ObjectProvider
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private final String jndiName;
-    private final Class homeInterface;
-    private final Properties properties;
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public SessionBeanProvider( String jndiName, Class homeInterface )
-    {
-        this.jndiName = jndiName;
-        this.homeInterface = homeInterface;
-        this.properties = null;
-    }
-
-    public SessionBeanProvider( String jndiName, Class homeInterface, Properties properties )
-    {
-        this.jndiName = jndiName;
-        this.homeInterface = homeInterface;
-        this.properties = properties;
-    }
-
-//**********************************************************************************************************************
-// ObjectProvider Implementation
-//**********************************************************************************************************************
-
-    public Object getObject()
-    {
-        try
-        {
-            final InitialContext initialContext = properties == null ? new InitialContext() :
-                    new InitialContext(properties);
-            Object homeObject = PortableRemoteObject.narrow(initialContext.lookup(jndiName), homeInterface);
-            final Method createMethod = homeObject.getClass().getMethod("create", ProxyUtils.EMPTY_ARGUMENT_TYPES);
-            return createMethod.invoke(homeObject, ProxyUtils.EMPTY_ARGUMENTS);
-        }
-        catch( NoSuchMethodException e )
-        {
-            throw new ObjectProviderException(
-                    "Unable to find no-arg create() method on home interface " + homeInterface.getName() + ".", e);
-        }
-        catch( IllegalAccessException e )
-        {
-            throw new ObjectProviderException(
-                    "No-arg create() method on home interface " + homeInterface.getName() + " is not accessible.",
-                    e); // Should never happen!
-        }
-        catch( NamingException e )
-        {
-            throw new ObjectProviderException("Unable to lookup EJB home object in JNDI.", e);
-        }
-        catch( InvocationTargetException e )
-        {
-            throw new ObjectProviderException(
-                    "No-arg create() method on home interface " + homeInterface.getName() + " threw an exception.", e);
-        }
-    }
-}
-
diff --git a/src/main/java/org/apache/commons/proxy/provider/remoting/package.html b/src/main/java/org/apache/commons/proxy/provider/remoting/package.html
deleted file mode 100644
index e906038..0000000
--- a/src/main/java/org/apache/commons/proxy/provider/remoting/package.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-
-<html>
-<body>
-<p>
-    This package contains some useful <a href="../../ObjectProvider.html">ObjectProvider</a> implementations for use
-    in remoting situations (EJB, RMI, Burlap, Hessian, JAX-RPC, etc).
-</p>
-</body>
-</html>
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
new file mode 100644
index 0000000..040dfe7
--- /dev/null
+++ b/src/site/markdown/index.md
@@ -0,0 +1,145 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+-->
+
+## Commons Proxy: Dynamic Proxies Made Easy
+
+  The *Proxy* design pattern ([GoF][]) allows you to provide
+  "a surrogate or placeholder for another object to control access to it".
+  Proxies can be used in many ways, some of which are:
+
+  * **Deferred Initialization** -
+     the proxy acts as a "stand-in" for the actual implementation allowing
+     it to be instantiated only when absolutely necessary.
+  * **Security** -
+     the proxy object can verify that the user actually has the permission to
+     execute the method (a la EJB).
+  * **Logging** -
+     the proxy can log evey method invocation, providing valuable debugging
+     information.
+  * **Performance Monitoring** -
+     the proxy can log each method invocation to a performance monitor allowing
+     system administrators to see what parts of the system are potentially
+     bogged down.
+
+  *Commons Proxy* supports dynamic proxy generation using proxy factories,
+  object providers, invokers, and interceptors.
+
+## Proxy Factories
+  A [ProxyFactory][] encapsulates all necessary proxying logic away from your
+  code. Switching proxying techniques/technologies is as simple as using a
+  different proxy factory implementation class.
+  *Commons Proxy* provides several proxy factory implementation modules:
+
+  * [commons-proxy2-jdk][]
+  * [commons-proxy2-cglib][]
+  * [commons-proxy2-javassist][]
+  * [commons-proxy2-asm4][]
+
+  Additionally, the core library provides a proxy factory
+  [implementation][defaultPF] that delegates to instances discoverable using
+  the Java [ServiceLoader][] mechanism (including those provided by the listed
+  modules).
+
+  Proxy factories allow you to create three different types of proxy objects:
+
+  * **Delegator Proxy** - delegates each method invocation to an object
+     provided by an [ObjectProvider][].
+  * **Interceptor Proxy** - allows an [Interceptor][] to intercept each
+     method invocation as it makes its way to the target of the invocation.
+  * **Invoker Proxy** - uses an [Invoker][] to handle all method invocations.
+
+## Object Providers
+  [Object providers][providers] provide the objects which will be the
+  "target" of a proxy. There are two types of object providers:
+
+### Core Object Providers
+  A core object provider provides a core implementation object.
+  *Commons Proxy* supports many different implementations including:
+
+  * **Constant** - Always returns a specific object
+  * **Bean** - Instantiates an object of a specified class each time
+  * **Cloning** - Reflectively calls the public `clone()` method
+                  on a `Cloneable` object
+
+### Decorating Object Providers
+  A decorating object provider decorates the object returned by another
+  provider. *Commons Proxy* provides a few implementations including:
+
+  * **Singleton** - Calls a nested provider at most once, returning that
+                    original value on all subsequent invocations
+
+## Invokers
+  An [Invoker][] handles all method invocations using a single method.
+  *Commons Proxy* provides a few invoker implementations:
+
+  * **Null** - Always returns a `null` (useful for the "Null Object" pattern)
+  * **Duck Typing** - Supports so-called "duck typing" by adapting a class to
+                      an interface it does not implement.
+  * **Invocation Handler Adapter** - Adapts an implementation of the JDK
+[InvocationHandler][] interface as a *Commons Proxy* [Invoker][].
+
+## Interceptors
+  *Commons Proxy* allows you to "intercept" a method invocation using
+  an [Interceptor][]. Interceptors provide *rudimentary* aspect-oriented
+  programming (AOP) support, allowing you to alter the parameters/results
+  of a method invocation without actually changing the implementation of
+  the method itself. *Commons Proxy* provides a few interceptor
+  implementations including:
+
+  * **ObjectProvider** - returns the value from an [ObjectProvider][]
+  * **Throwing** - throws an exception
+  * **Switch** - provides a fluent API to configure the handling
+                 of invoked methods
+
+## Releases
+  The latest version is v1.0. - [Download now!][download]
+
+  For previous releases, see the [Apache archive][archive].
+
+  _**Note:** The 1.x releases are compatible with JDK1.4+._
+
+## Support
+  The [Commons mailing lists][mailing-lists] act as the main support forum.
+  The `user` list is suitable for most library usage queries.
+  The `dev` list is intended for the development discussion.
+  Please remember that the lists are shared between all Commons components,
+  so prefix your email subject with `[proxy]`.
+
+  Issues may be reported via [ASF JIRA][issue-tracking]. Please read the
+  instructions carefully to submit a useful bug report or enhancement request.
+
+[download]: http://commons.apache.org/downloads/download_proxy.cgi
+[archive]: http://archive.apache.org/dist/commons/proxy/
+[mailing-lists]: mail-lists.html
+[issue-tracking]: issue-tracking.html
+
+[commons-proxy2-jdk]: commons-proxy2-jdk/index.html
+[commons-proxy2-cglib]: commons-proxy2-cglib/index.html
+[commons-proxy2-javassist]: commons-proxy2-javassist/index.html
+[commons-proxy2-asm4]: commons-proxy2-asm4/index.html
+[ProxyFactory]: apidocs/org/apache/commons/proxy2/ProxyFactory.html
+[ObjectProvider]: apidocs/org/apache/commons/proxy2/ObjectProvider.html
+[Interceptor]: apidocs/org/apache/commons/proxy2/Interceptor.html
+[Invoker]: apidocs/org/apache/commons/proxy2/Invoker.html
+[defaultPF]: apidocs/org/apache/commons/proxy2/ProxyUtils.html#proxyFactory\(\)
+[providers]: apidocs/org/apache/commons/proxy2/provider/package-summary.html
+
+[ServiceLoader]: http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html
+[InvocationHandler]: http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/InvocationHandler.html
+[GoF]: http://www.amazon.com/exec/obidos/tg/detail/-/0201633612/qid=1125413337/sr=1-1/ref=sr_1_1/104-0714405-6441551?v=glance&amp;s=books
diff --git a/src/site/resources/download_proxy.cgi b/src/site/resources/download_proxy.cgi
deleted file mode 100644
index 495cde1..0000000
--- a/src/site/resources/download_proxy.cgi
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# Just call the standard mirrors.cgi script. It will use download.html
-# as the input template.
-exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $*
\ No newline at end of file
diff --git a/src/site/site.xml b/src/site/site.xml
index 13ce200..143f2f3 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
+
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one or more
   ~ contributor license agreements.  See the NOTICE file distributed with
@@ -15,6 +16,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
+
 <project name="Commons Proxy">
   <bannerRight>
     <name>Commons Proxy</name>
@@ -24,7 +26,6 @@
   <body>
     <menu name="Commons Proxy">
       <item name="Overview" href="index.html"/>
-      <item name="Download" href="http://commons.apache.org/proxy/download_proxy.cgi"/>
       <item name="Mailing lists" href="/mail-lists.html"/>
       <item name="Issue Tracking" href="/issue-tracking.html"/>
       <item name="Team" href="/team-list.html"/>
diff --git a/src/site/xdoc/download_proxy.xml b/src/site/xdoc/download_proxy.xml
index 2ac93b4..41af3db 100644
--- a/src/site/xdoc/download_proxy.xml
+++ b/src/site/xdoc/download_proxy.xml
@@ -1,20 +1,21 @@
 <?xml version="1.0"?>
 <!--
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You 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
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
 
-     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.
--->
 <!--
  +======================================================================+
  |****                                                              ****|
@@ -50,7 +51,6 @@
   </properties>
   <body>
     <section name="Download Commons Proxy">
-    <subsection name="Using a Mirror">
       <p>
         We recommend you use a mirror to download our release
         builds, but you <strong>must</strong> verify the integrity of
@@ -88,51 +88,59 @@
       </form>
 
       <p>
-        The <a href="http://www.apache.org/dist/commons/KEYS">KEYS</a>
-        link links to the code signing keys used to sign the product.
+        The <code>KEYS</code> link links to the code signing keys used to sign the product.
         The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site. 
         The <code>MD5</code> link downloads the checksum from the main site.
       </p>
-    </subsection>
-    </section>
-    <section name="Commons Proxy 1.0 ">
-      <subsection name="Binaries">
-        <table>
-          <tr>
-              <td><a href="[preferred]/commons/proxy/binaries/commons-proxy-1.0.tar.gz">commons-proxy-1.0.tar.gz</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.tar.gz.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.tar.gz.asc">pgp</a></td>
-          </tr>
-          <tr>
-              <td><a href="[preferred]/commons/proxy/binaries/commons-proxy-1.0.zip">commons-proxy-1.0.zip</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.zip.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.zip.asc">pgp</a></td>
-          </tr>
-        </table>
-      </subsection>
-      <subsection name="Source">
-        <table>
-          <tr>
-              <td><a href="[preferred]/commons/proxy/source/commons-proxy-1.0-src.tar.gz">commons-proxy-1.0-src.tar.gz</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.tar.gz.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.tar.gz.asc">pgp</a></td>
-          </tr>
-          <tr>
-              <td><a href="[preferred]/commons/proxy/source/commons-proxy-1.0-src.zip">commons-proxy-1.0-src.zip</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.zip.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.zip.asc">pgp</a></td>
-          </tr>
-        </table>
-      </subsection>
-    </section>
-    <section name="Archives">
-        <p>
-          Older releases can be obtained from the archives.
-        </p>
-        <ul>
+
+      <p>
+        For more information concerning Commons Proxy, see the 
+        <a href="index.html" class="name">Commons Proxy</a> web site.
+      </p>
+
+      <p>
+        <div class="links"><span class="link"><a href="http://www.apache.org/dist/commons/KEYS">KEYS</a></span></div>
+        <ul class="downloads">
+          <li class="group"><div class="links"><span class="label">Binary</span></div>
+            <ul>
+              <li class="download"><a href="[preferred]/commons/proxy/binaries/commons-proxy-1.0.tar.gz">1.0.tar.gz</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.tar.gz.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.tar.gz.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+              <li class="download"><a href="[preferred]/commons/proxy/binaries/commons-proxy-1.0.zip">1.0.zip</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.zip.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/proxy/binaries/commons-proxy-1.0.zip.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+            </ul>
+          </li>
+          <li class="group"><div class="links"><span class="label">Source</span></div>
+            <ul>
+              <li class="download"><a href="[preferred]/commons/proxy/source/commons-proxy-1.0-src.tar.gz">1.0.tar.gz</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.tar.gz.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.tar.gz.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+              <li class="download"><a href="[preferred]/commons/proxy/source/commons-proxy-1.0-src.zip">1.0.zip</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.zip.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/proxy/source/commons-proxy-1.0-src.zip.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+            </ul>
+          </li>
           <li class="download"><a href="[preferred]/commons/proxy/">browse download area</a></li>
           <li><a href="http://archive.apache.org/dist/commons/proxy/">archives...</a></li>
         </ul>
+      </p>
     </section>
   </body>
 </document>
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
deleted file mode 100644
index 4e4d0a4..0000000
--- a/src/site/xdoc/index.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You 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.
-  -->
-<document>
-    <properties>
-        <title>Overview</title>
-        <author email="dev AT commons DOT apache DOT org">Apache Commons Development Team</author>
-        <author email="jcarman AT apache DOT org">James Carman</author>
-    </properties>
-
-    <body>
-        <section name="Commons Proxy: Dynamic Proxies Made Easy">
-            <p>
-                The <em>Proxy</em> design pattern (<a href="http://www.amazon.com/exec/obidos/tg/detail/-/0201633612/qid=1125413337/sr=1-1/ref=sr_1_1/104-0714405-6441551?v=glance&amp;s=books">GoF</a>)
-                allows you to provide &quot;a surrogate or placeholder for another object to control access to it&quot;.
-                Proxies can be used in many ways.  Some of which are:
-            </p>
-                <ul>
-                    <li><b>Deferred Initialization</b> - the proxy acts as a "stand-in" for the actual implementation allowing
-                    it to be instantiated only when absolutely necessary.</li>
-                    <li><b>Security</b> - the proxy object can verify that the user actually has the permission to execute
-                    the method (a la EJB).</li>
-                    <li><b>Logging</b> - the proxy can log evey method invocation, providing valuable debugging information.</li>
-                    <li><b>Performance Monitoring</b> - the proxy can log each method invocation to a performance monitor
-                    allowing system administrators to see what parts of the system are potentially bogged down.</li>
-                </ul>
-            <p>
-                <em>Commons Proxy</em> supports dynamic proxy generation using proxy factories, object providers, invokers, and
-                interceptors.
-            </p>
-            <section name="Proxy Factories">
-                <p>
-                    <a href="apidocs/org/apache/commons/proxy/ProxyFactory.html">Proxy factories</a>
-                    encapsulate all necessary proxying logic away from your code.  Switching proxying
-                    techniques/technologies is as simple as using a different proxy factory implementation class.
-                    Currently,  <em>Commons Proxy</em> provides proxy factory implementations using JDK proxies,
-                    <a href="http://cglib.sourceforge.net">CGLIB</a>, and
-                    <a href="http://www.jboss.org/products/javassist">Javassist</a>.  Proxy factories allow you to create
-                    three different types of proxy objects:
-            </p>
-                    <ul>
-                        <li><b>Delegator Proxies</b> - a proxy that merely delegates each method invocation to an
-                            object provided by an <a href="apidocs/org/apache/commons/proxy/ObjectProvider.html">object provider</a>.</li>
-                        <li><b>Interceptor Proxies</b> - a proxy that allows an <a href="apidocs/org/apache/commons/proxy/Interceptor.html">Interceptor</a> to intercept each
-                            method invocation as it makes its way to the target of the invocation.</li>
-                        <li><b>Invoker Proxies</b> - a proxy that uses an
-                            <a href="apidocs/org/apache/commons/proxy/Invoker.html">invoker</a> to handle all method
-                            invocations.</li>
-                    </ul>
-            </section>
-            <section name="Object Providers">
-                <p>
-                    <a href="apidocs/org/apache/commons/proxy/provider/package-summary.html">Object providers</a>
-                    provide the
-                    objects which will be the &quot;target&quot; of a proxy. There are two types of object providers:
-                    <subsection name="Core Object Providers">
-                        <p>
-                        A core object provider provides a core implementation object.  <em>Commons Proxy</em> supports
-                        many different implementations including:
-                        </p>
-                        <table border="0">
-                            <tr><td><b>Constant</b></td><td>Always returns a specific object</td></tr>
-                            <tr><td><b>Bean</b></td><td>Merely instantiates an object of a specified class each time</td></tr>
-                            <tr><td><b>Cloning</b></td><td>Reflectively calls the public clone() method on a Cloneable object</td></tr>
-                            <tr><td><b>Hessian</b></td><td>Returns a <a href="http://www.caucho.com/hessian/index.xtp">Hessian</a>-based service object</td></tr>
-                            <tr><td><b>Burlap</b></td><td>Returns a <a href="http://www.caucho.com/burlap/index.xtp">Burlap</a>-based service object</td></tr>
-                            <tr><td><b>JAX-RPC</b></td><td>Returns a <a href="http://java.sun.com/webservices/jaxrpc/index.jsp">JAX-RPC</a>-based service object</td></tr>
-                            <tr><td><b>Session Bean</b></td><td>Returns a reference to a Session EJB (stateless session beans only)</td></tr>
-                        </table>
-                    </subsection>
-                    <subsection name="Decorating Object Providers">
-                        <p>
-                        A decorating object provider decorates the object returned by another provider.
-                            <em>Commons Proxy</em> provides a few implementations including:
-                        </p>
-                        <table border="0">
-                            <tr><td><b>Singleton</b></td><td>Calls a nested provider at most once, returning that original value on all subsequent invocations</td></tr>
-                        </table>
-
-                    </subsection>
-
-                </p>
-            </section>
-            <section name="Invokers">
-                <p>
-                    An <a href="apidocs/org/apache/commons/proxy/Invoker.html">invoker</a> handles all
-                    method invocations using a single method.  <em>Commons Proxy</em> provides a few invoker implementations:
-                </p>
-                    <table border="0">
-                      <tr><td><b>Null</b></td><td>Always returns a null (useful for the "Null Object" pattern)</td></tr>
-                      <tr><td><b>Apache XML-RPC</b></td><td>Uses <a href="http://ws.apache.org/xmlrpc/">Apache XML-RPC</a> to fulfill the method invocation</td></tr>
-                        <tr><td><b>Duck Typing</b></td><td>Supports &quot;duck typing&quot; by adapting a class to an interface it does not implement.</td></tr>
-                      <tr><td><b>Invocation Handler Adapter</b></td><td>Adapts the JDK <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/InvocationHandler.html">InvocationHandler</a> interface
-                          to the <em>Commons Proxy</em> <a href="apidocs/org/apache/commons/proxy/Invoker.html">Invoker</a> interface.</td></tr>
-                    </table>
-
-            </section>
-            <section name="Interceptors">
-                <p>
-                    <em>Commons Proxy</em> allows you to &quot;intercept&quot; a method invocation using <a href="apidocs/org/apache/commons/proxy/Interceptor.html">Interceptors</a>.
-                    Interceptors provide <em>rudimentary</em> aspect-oriented
-                    programming support, allowing you to alter the results/effects of a method invocation without actually
-                    changing the implementation of the method itself.  <em>Commons Proxy</em> provides a few interceptor
-                    implementations including:
-                </p>
-                    <table border="0">
-                      <tr><td><b>Executor</b></td><td>Uses an
-                      <a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/Executor.html">Executor</a> to execute the method in possibly another thread (only void methods are supported).</td></tr>
-                      <tr><td><b>Logging</b></td><td>Logs all method invocations using the
-                      <a href="http://commons.apache.org/logging/">Apache Commons Logging</a> API</td></tr>
-                      <tr><td><b>Filtered</b></td><td>Optionally intercepts a method invocation based on a
-                      <a href="apidocs/org/apache/commons/proxy/interceptor/MethodFilter.html">method filter</a></td></tr>
-                      <tr><td><b>Method Interceptor Adapter</b></td><td>Adapts the AOP Alliance <a href="http://aopalliance.sourceforge.net/doc/org/aopalliance/intercept/MethodInterceptor.html">MethodInterceptor</a> interface to the
-                          <em>Commons Proxy</em> <a href="apidocs/org/apache/commons/proxy/Interceptor.html">Interceptor</a> interface.</td></tr>
-
-                      </table>
-            </section>
-        </section>
-        <section name="Releases">
-            <p>
-                The latest version is v1.0. -
-                <a href="http://commons.apache.org/proxy/download_proxy.cgi">Download now!</a><br />
-            </p>
-            <p>
-                For previous releases, see the <a href="http://archive.apache.org/dist/commons/proxy/">Apache Archive</a>
-            </p>
-            <p>
-                <i><b>Note:</b> The 1.x releases are compatible with JDK1.4+.</i>
-            </p>
-        </section>
-        <section name="Support">
-            <p>
-                The <a href="mail-lists.html">commons mailing lists</a> act as the main support forum.
-                The user list is suitable for most library usage queries.
-                The dev list is intended for the development discussion.
-                Please remember that the lists are shared between all commons components,
-                so prefix your email subject with [proxy].
-            </p>
-            <p>
-                Issues may be reported via <a href="issue-tracking.html">ASF JIRA</a>.
-                Please read the instructions carefully to submit a useful bug report or enhancement request.
-            </p>
-        </section>
-    </body>
-</document>
diff --git a/src/site/xdoc/issue-tracking.xml b/src/site/xdoc/issue-tracking.xml
index b55530c..985210a 100644
--- a/src/site/xdoc/issue-tracking.xml
+++ b/src/site/xdoc/issue-tracking.xml
@@ -1,20 +1,21 @@
 <?xml version="1.0"?>
 <!--
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You 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
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
 
-     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.
--->
 <!--
  +======================================================================+
  |****                                                              ****|
diff --git a/src/site/xdoc/mail-lists.xml b/src/site/xdoc/mail-lists.xml
deleted file mode 100644
index e0f628d..0000000
--- a/src/site/xdoc/mail-lists.xml
+++ /dev/null
@@ -1,202 +0,0 @@
-<?xml version="1.0"?>
-<!--
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You 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.
--->
-<!--
- +======================================================================+
- |****                                                              ****|
- |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
- |****                    DO NOT EDIT DIRECTLY                      ****|
- |****                                                              ****|
- +======================================================================+
- | TEMPLATE FILE: mail-lists-template.xml                               |
- | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
- +======================================================================+
- |                                                                      |
- | 1) Re-generate using: mvn commons:mail-page                          |
- |                                                                      |
- | 2) Set the following properties in the component's pom:              |
- |    - commons.componentid (required, alphabetic, lower case)          |
- |                                                                      |
- | 3) Example Properties                                                |
- |                                                                      |
- |  <properties>                                                        |
- |    <commons.componentid>math</commons.componentid>                   |
- |  </properties>                                                       |
- |                                                                      |
- +======================================================================+
--->
-<document>
-  <properties>
-    <title>Commons Proxy Mailing Lists</title>
-    <author email="dev@commons.apache.org">Commons Documentation Team</author>
-  </properties>
-  <body>
-
-    <section name="Overview">
-      <p>
-        <a href="index.html">Commons Proxy</a> shares mailing lists with all the other 
-        <a href="http://commons.apache.org/components.html">Commons Components</a>.
-        To make it easier for people to only read messages related to components they are interested in,
-        the convention in Commons is to prefix the subject line of messages with the component's name,
-        for example:
-        <ul>
-          <li>[proxy] Problem with the ...</li>
-        </ul>
-      </p>
-      <p>
-        Questions related to the usage of Commons Proxy should be posted to the
-        <a href="http://mail-archives.apache.org/mod_mbox/commons-user/">User List</a>.
-        <br />
-        The <a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">Developer List</a>
-        is for questions and discussion related to the development of Commons Proxy.
-        <br />
-        Please do not cross-post; developers are also subscribed to the user list.
-      </p>
-      <p>
-        <strong>Note:</strong> please don't send patches or attachments to any of the mailing lists.
-        Patches are best handled via the <a href="issue-tracking.html">Issue Tracking</a> system. 
-        Otherwise, please upload the file to a public server and include the URL in the mail. 
-      </p>
-    </section>
-
-    <section name="Commons Proxy Mailing Lists">
-      <p>
-        <strong>Please prefix the subject line of any messages for <a href="index.html">Commons Proxy</a>
-        with <i>[proxy]</i></strong> - <i>thanks!</i>
-        <br />
-        <br />
-      </p>
-
-      <table>
-        <tr>
-          <th>Name</th>
-          <th>Subscribe</th>
-          <th>Unsubscribe</th>
-          <th>Post</th>
-          <th>Archive</th>
-          <th>Other Archives</th>
-        </tr>
-
-
-        <tr>
-          <td>
-            <strong>Commons User List</strong>
-            <br /><br />
-            Questions on using Commons Proxy.
-            <br /><br />
-          </td>
-          <td><a href="mailto:user-subscribe@commons.apache.org">Subscribe</a></td>
-          <td><a href="mailto:user-unsubscribe@commons.apache.org">Unsubscribe</a></td>
-          <td><a href="mailto:user@commons.apache.org?subject=[proxy]">Post</a></td>
-          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-user/">mail-archives.apache.org</a></td>
-          <td><a href="http://markmail.org/list/org.apache.commons.users/">markmail.org</a><br />
-              <a href="http://www.mail-archive.com/user@commons.apache.org/">www.mail-archive.com</a><br />
-              <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
-          </td>
-        </tr>
-
-
-        <tr>
-          <td>
-            <strong>Commons Developer List</strong>
-            <br /><br />
-            Discussion of development of Commons Proxy.
-            <br /><br />
-          </td>
-          <td><a href="mailto:dev-subscribe@commons.apache.org">Subscribe</a></td>
-          <td><a href="mailto:dev-unsubscribe@commons.apache.org">Unsubscribe</a></td>
-          <td><a href="mailto:dev@commons.apache.org?subject=[proxy]">Post</a></td>
-          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">mail-archives.apache.org</a></td>
-          <td><a href="http://markmail.org/list/org.apache.commons.dev/">markmail.org</a><br />
-              <a href="http://www.mail-archive.com/dev@commons.apache.org/">www.mail-archive.com</a><br />
-              <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
-          </td>
-        </tr>
-
-
-        <tr>
-          <td>
-            <strong>Commons Issues List</strong>
-            <br /><br />
-            Only for e-mails automatically generated by the <a href="issue-tracking.html">issue tracking</a> system.
-            <br /><br />
-          </td>
-          <td><a href="mailto:issues-subscribe@commons.apache.org">Subscribe</a></td>
-          <td><a href="mailto:issues-unsubscribe@commons.apache.org">Unsubscribe</a></td>
-          <td><i>read only</i></td>
-          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-issues/">mail-archives.apache.org</a></td>
-          <td><a href="http://markmail.org/list/org.apache.commons.issues/">markmail.org</a><br />
-              <a href="http://www.mail-archive.com/issues@commons.apache.org/">www.mail-archive.com</a>
-          </td>
-        </tr>
-
-
-        <tr>
-          <td>
-            <strong>Commons Commits List</strong>
-            <br /><br />
-            Only for e-mails automatically generated by the <a href="source-repository.html">source control</a> sytem.
-            <br /><br />
-          </td>
-          <td><a href="mailto:commits-subscribe@commons.apache.org">Subscribe</a></td>
-          <td><a href="mailto:commits-unsubscribe@commons.apache.org">Unsubscribe</a></td>
-          <td><i>read only</i></td>
-          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-commits/">mail-archives.apache.org</a></td>
-          <td><a href="http://markmail.org/list/org.apache.commons.commits/">markmail.org</a><br />
-              <a href="http://www.mail-archive.com/commits@commons.apache.org/">www.mail-archive.com</a>
-          </td>
-        </tr>
-
-      </table>
-
-    </section>
-    <section name="Apache Mailing Lists">
-      <p>
-        Other mailing lists which you may find useful include:
-      </p>
-
-      <table>
-        <tr>
-          <th>Name</th>
-          <th>Subscribe</th>
-          <th>Unsubscribe</th>
-          <th>Post</th>
-          <th>Archive</th>
-          <th>Other Archives</th>
-        </tr>
-        <tr>
-          <td>
-            <strong>Apache Announce List</strong>
-            <br /><br />
-            General announcements of Apache project releases.
-            <br /><br />
-          </td>
-          <td><a class="externalLink" href="mailto:announce-subscribe@apache.org">Subscribe</a></td> 
-          <td><a class="externalLink" href="mailto:announce-unsubscribe@apache.org">Unsubscribe</a></td> 
-          <td><i>read only</i></td>
-          <td><a class="externalLink" href="http://mail-archives.apache.org/mod_mbox/www-announce/">mail-archives.apache.org</a></td> 
-          <td><a class="externalLink" href="http://markmail.org/list/org.apache.announce/">markmail.org</a><br />
-              <a class="externalLink" href="http://old.nabble.com/Apache-News-and-Announce-f109.html">old.nabble.com</a><br />
-              <a class="externalLink" href="http://www.mail-archive.com/announce@apache.org/">www.mail-archive.com</a><br />
-              <a class="externalLink" href="http://news.gmane.org/gmane.comp.apache.announce">news.gmane.org</a>
-          </td>
-        </tr>
-      </table>
-
-    </section>
-  </body>
-</document>
diff --git a/src/test/java/org/apache/commons/proxy/TestProxyFactory.java b/src/test/java/org/apache/commons/proxy/TestProxyFactory.java
deleted file mode 100644
index adcb216..0000000
--- a/src/test/java/org/apache/commons/proxy/TestProxyFactory.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy;
-
-import org.apache.commons.proxy.factory.AbstractProxyFactoryTestCase;
-
-public class TestProxyFactory extends AbstractProxyFactoryTestCase
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public TestProxyFactory()
-    {
-        super(new ProxyFactory());
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java b/src/test/java/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java
deleted file mode 100644
index f9e74d7..0000000
--- a/src/test/java/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.Invocation;
-import org.apache.commons.proxy.Invoker;
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.ProxyFactory;
-import org.apache.commons.proxy.provider.BeanProvider;
-import org.apache.commons.proxy.provider.ConstantProvider;
-import org.apache.commons.proxy.provider.SingletonProvider;
-import org.apache.commons.proxy.util.AbstractTestCase;
-import org.apache.commons.proxy.util.DuplicateEcho;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-import org.apache.commons.proxy.util.SuffixInterceptor;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.Date;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public abstract class AbstractProxyFactoryTestCase extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static final Class[] ECHO_ONLY = new Class[]{Echo.class};
-    protected final ProxyFactory factory;
-    private static final Class[] COMPARABLE_ONLY = new Class[] { Comparable.class };
-
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    protected AbstractProxyFactoryTestCase(ProxyFactory factory)
-    {
-        this.factory = factory;
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    private ObjectProvider createSingletonEcho()
-    {
-        return new SingletonProvider(new BeanProvider(EchoImpl.class));
-    }
-
-    public void testInterceptorHashCode()
-    {
-        final Echo proxy = (Echo) factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
-        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));
-    }
-
-    public void testInvokerHashCode() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
-        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));
-    }
-
-    public void testDelegatorHashCode() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), ECHO_ONLY);
-        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));
-    }
-
-
-    public void testInterceptorEquals()
-    {
-        final Date date = new Date();
-        final Comparable proxy1 = (Comparable) factory.createInterceptorProxy(date,
-                new NoOpMethodInterceptor(), COMPARABLE_ONLY);
-        final Comparable proxy2 = (Comparable) factory.createInterceptorProxy(date,
-                new NoOpMethodInterceptor(), COMPARABLE_ONLY);
-        assertEquals(proxy1, proxy1);
-        assertFalse(proxy1.equals(proxy2));
-        assertFalse(proxy2.equals(proxy1));
-    }
-
-    public void testInvokerEquals() throws Exception
-    {
-        final Comparable proxy1 = (Comparable) factory.createInvokerProxy(new InvokerTester(), COMPARABLE_ONLY);
-        final Comparable proxy2 = (Comparable) factory.createInvokerProxy(new InvokerTester(), COMPARABLE_ONLY);
-        assertEquals(proxy1, proxy1);
-        assertFalse(proxy1.equals(proxy2));
-        assertFalse(proxy2.equals(proxy1));
-    }
-
-    public void testDelegatorEquals() throws Exception
-    {
-        final Date date = new Date();
-        final Comparable proxy1 = (Comparable) factory.createDelegatorProxy(new ConstantProvider(date),
-                COMPARABLE_ONLY);
-        final Comparable proxy2 = (Comparable) factory.createDelegatorProxy(new ConstantProvider(date),
-                COMPARABLE_ONLY);
-        assertEquals(proxy1, proxy1);
-        assertFalse(proxy1.equals(proxy2));
-        assertFalse(proxy2.equals(proxy1));
-    }
-
-    public void testBooleanInterceptorParameter()
-    {
-        final Echo echo = (Echo) factory.createInterceptorProxy(new EchoImpl(), new InterceptorTester(), ECHO_ONLY);
-        assertFalse(echo.echoBack(false));
-        assertTrue(echo.echoBack(true));
-    }
-
-    public void testCanProxy()
-    {
-        assertTrue(factory.canProxy(ECHO_ONLY));
-        assertFalse(factory.canProxy(new Class[]{EchoImpl.class}));
-    }
-
-    public void testChangingArguments()
-    {
-        final Echo proxy = (Echo) factory.createInterceptorProxy(new EchoImpl(), new ChangeArgumentInterceptor(), ECHO_ONLY);
-        assertEquals("something different", proxy.echoBack("whatever"));
-    }
-
-    public void testCreateDelegatingProxy()
-    {
-        final Echo echo = (Echo) factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);
-        echo.echo();
-        assertEquals("message", echo.echoBack("message"));
-        assertEquals("ab", echo.echoBack("a", "b"));
-    }
-
-    public void testCreateInterceptorProxy()
-    {
-        final Echo target = (Echo) factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);
-        final Echo proxy = (Echo) factory.createInterceptorProxy(target, new SuffixInterceptor(" suffix"), ECHO_ONLY);
-        proxy.echo();
-        assertEquals("message suffix", proxy.echoBack("message"));
-    }
-
-    public void testDelegatingProxyClassCaching() throws Exception
-    {
-        final Echo proxy1 = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), ECHO_ONLY);
-        final Echo proxy2 = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), ECHO_ONLY);
-        assertNotSame(proxy1, proxy2);
-        assertSame(proxy1.getClass(), proxy2.getClass());
-    }
-
-    public void testDelegatingProxyInterfaceOrder()
-    {
-        final Echo echo = (Echo) factory.createDelegatorProxy(createSingletonEcho(), new Class[]{Echo.class, DuplicateEcho.class});
-        final List expected = new LinkedList(Arrays.asList(new Class[]{Echo.class, DuplicateEcho.class}));
-        final List actual = new LinkedList(Arrays.asList(echo.getClass().getInterfaces()));
-        actual.retainAll(expected);  // Doesn't alter order!
-        assertEquals(expected, actual);
-    }
-
-    public void testDelegatingProxySerializable() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), ECHO_ONLY);
-        assertSerializable(proxy);
-    }
-
-    public void testInterceptingProxyClassCaching() throws Exception
-    {
-        final Echo proxy1 = (Echo) factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
-        final Echo proxy2 = (Echo) factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
-        assertNotSame(proxy1, proxy2);
-        assertSame(proxy1.getClass(), proxy2.getClass());
-    }
-
-    public void testInterceptingProxySerializable() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
-        assertSerializable(proxy);
-    }
-
-    public void testInterceptorProxyWithCheckedException() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
-        try
-        {
-            proxy.ioException();
-            fail();
-        }
-        catch (IOException e)
-        {
-        }
-    }
-
-    public void testInterceptorProxyWithUncheckedException() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
-        try
-        {
-            proxy.illegalArgument();
-            fail();
-        }
-        catch (IllegalArgumentException e)
-        {
-        }
-    }
-
-    public void testInterfaceHierarchies()
-    {
-        final SortedSet set = (SortedSet) factory.createDelegatorProxy(new ConstantProvider(new TreeSet()), new Class[]{SortedSet.class});
-        set.add("Hello");
-    }
-
-    public void testInvokerProxy() throws Exception
-    {
-        final InvokerTester tester = new InvokerTester();
-        final Echo echo = (Echo) factory.createInvokerProxy(tester, ECHO_ONLY);
-        echo.echoBack("hello");
-        assertEquals(Echo.class.getMethod("echoBack", new Class[]{String.class}), tester.method);
-        assertSame(echo, tester.proxy);
-        assertNotNull(tester.args);
-        assertEquals(1, tester.args.length);
-        assertEquals("hello", tester.args[0]);
-    }
-
-    public void testInvokerProxyClassCaching() throws Exception
-    {
-        final Echo proxy1 = (Echo) factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
-        final Echo proxy2 = (Echo) factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
-        assertNotSame(proxy1, proxy2);
-        assertSame(proxy1.getClass(), proxy2.getClass());
-    }
-
-    public void testInvokerProxySerializable() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
-        assertSerializable(proxy);
-    }
-
-    public void testMethodInvocationClassCaching() throws Exception
-    {
-        final InterceptorTester tester = new InterceptorTester();
-        final EchoImpl target = new EchoImpl();
-        final Echo proxy1 = (Echo) factory.createInterceptorProxy(target, tester, ECHO_ONLY);
-        final Echo proxy2 = (Echo) factory.createInterceptorProxy(target, tester, new Class[]{Echo.class, DuplicateEcho.class});
-        proxy1.echoBack("hello1");
-        final Class invocationClass1 = tester.invocationClass;
-        proxy2.echoBack("hello2");
-        assertSame(invocationClass1, tester.invocationClass);
-    }
-
-    public void testMethodInvocationDuplicateMethods() throws Exception
-    {
-        final InterceptorTester tester = new InterceptorTester();
-        final EchoImpl target = new EchoImpl();
-        final Echo proxy = (Echo) factory.createInterceptorProxy(target, tester, new Class[]{Echo.class, DuplicateEcho.class});
-        proxy.echoBack("hello");
-        assertEquals(Echo.class.getMethod("echoBack", new Class[]{String.class}), tester.method);
-    }
-
-    public void testMethodInvocationImplementation() throws Exception
-    {
-        final InterceptorTester tester = new InterceptorTester();
-        final EchoImpl target = new EchoImpl();
-        final Echo proxy = (Echo) factory.createInterceptorProxy(target, tester, ECHO_ONLY);
-        proxy.echo();
-        assertNotNull(tester.arguments);
-        assertEquals(0, tester.arguments.length);
-        assertEquals(Echo.class.getMethod("echo", new Class[]{}), tester.method);
-        assertEquals(target, tester.proxy);
-        proxy.echoBack("Hello");
-        assertNotNull(tester.arguments);
-        assertEquals(1, tester.arguments.length);
-        assertEquals("Hello", tester.arguments[0]);
-        assertEquals(Echo.class.getMethod("echoBack", new Class[]{String.class}), tester.method);
-        proxy.echoBack("Hello", "World");
-        assertNotNull(tester.arguments);
-        assertEquals(2, tester.arguments.length);
-        assertEquals("Hello", tester.arguments[0]);
-        assertEquals("World", tester.arguments[1]);
-        assertEquals(Echo.class.getMethod("echoBack", new Class[]{String.class, String.class}), tester.method);
-    }
-
-    public void testPrimitiveParameter()
-    {
-        final Echo echo = (Echo) factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);
-        assertEquals(1, echo.echoBack(1));
-    }
-
-    public void testProxyWithCheckedException() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), ECHO_ONLY);
-        try
-        {
-            proxy.ioException();
-            fail();
-        }
-        catch (IOException e)
-        {
-        }
-    }
-
-    public void testProxyWithUncheckedException() throws Exception
-    {
-        final Echo proxy = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), ECHO_ONLY);
-        try
-        {
-            proxy.illegalArgument();
-            fail();
-        }
-        catch (IllegalArgumentException e)
-        {
-        }
-    }
-
-    public void testWithNonAccessibleTargetType()
-    {
-        final Echo proxy = (Echo) factory.createInterceptorProxy(new PrivateEcho(), new NoOpMethodInterceptor(), ECHO_ONLY);
-        proxy.echo();
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private static class ChangeArgumentInterceptor implements Interceptor
-    {
-        public Object intercept(Invocation methodInvocation) throws Throwable
-        {
-            methodInvocation.getArguments()[0] = "something different";
-            return methodInvocation.proceed();
-        }
-    }
-
-    protected static class InterceptorTester implements Interceptor
-    {
-        private Object[] arguments;
-        private Method method;
-        private Object proxy;
-        private Class invocationClass;
-
-        public Object intercept(Invocation methodInvocation) throws Throwable
-        {
-            arguments = methodInvocation.getArguments();
-            method = methodInvocation.getMethod();
-            proxy = methodInvocation.getProxy();
-            invocationClass = methodInvocation.getClass();
-            return methodInvocation.proceed();
-        }
-    }
-
-    protected static class InvokerTester implements Invoker
-    {
-        private Object method;
-        private Object[] args;
-        private Object proxy;
-
-        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
-        {
-            this.proxy = proxy;
-            this.method = method;
-            this.args = args;
-            return null;
-        }
-    }
-
-    protected static class NoOpMethodInterceptor implements Interceptor, Serializable
-    {
-        public Object intercept(Invocation methodInvocation) throws Throwable
-        {
-            return methodInvocation.proceed();
-        }
-    }
-
-    private static class PrivateEcho extends EchoImpl
-    {
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/factory/AbstractSubclassingProxyFactoryTestCase.java b/src/test/java/org/apache/commons/proxy/factory/AbstractSubclassingProxyFactoryTestCase.java
deleted file mode 100644
index 9733f58..0000000
--- a/src/test/java/org/apache/commons/proxy/factory/AbstractSubclassingProxyFactoryTestCase.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory;
-
-import org.apache.commons.proxy.ProxyFactory;
-import org.apache.commons.proxy.exception.ProxyFactoryException;
-import org.apache.commons.proxy.invoker.NullInvoker;
-import org.apache.commons.proxy.provider.ConstantProvider;
-import org.apache.commons.proxy.util.AbstractEcho;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-
-import java.util.Date;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public abstract class AbstractSubclassingProxyFactoryTestCase extends AbstractProxyFactoryTestCase
-{
-    private static final Class[] DATE_ONLY = new Class[]{Date.class};
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    protected AbstractSubclassingProxyFactoryTestCase(ProxyFactory factory)
-    {
-        super(factory);
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testCanProxy()
-    {
-        assertTrue(factory.canProxy(new Class[]{Echo.class}));
-        assertTrue(factory.canProxy(new Class[]{EchoImpl.class}));
-        assertFalse(factory.canProxy(new Class[]{FinalEcho.class}));
-        assertTrue(factory.canProxy(new Class[]{FinalMethodEcho.class, Echo.class}));
-        assertFalse(factory.canProxy(new Class[]{NoDefaultConstructorEcho.class}));
-        assertTrue(factory.canProxy(new Class[]{ProtectedConstructorEcho.class}));
-        assertFalse(factory.canProxy(new Class[]{InvisibleEcho.class}));
-        assertFalse(factory.canProxy(new Class[]{Echo.class, EchoImpl.class, String.class}));
-    }
-
-    public void testDelegatorWithMultipleSuperclasses()
-    {
-        try
-        {
-            factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()),
-                    new Class[]{EchoImpl.class, String.class});
-            fail();
-        }
-        catch (ProxyFactoryException e)
-        {
-        }
-    }
-
-    public void testDelegatorWithSuperclass()
-    {
-        final Echo echo = (Echo) factory
-                .createDelegatorProxy(new ConstantProvider(new EchoImpl()), new Class[]{Echo.class, EchoImpl.class});
-        assertTrue(echo instanceof EchoImpl);
-    }
-
-    public void testInterceptorWithMultipleSuperclasses()
-    {
-        try
-        {
-            factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(),
-                    new Class[]{EchoImpl.class, String.class});
-            fail();
-        }
-        catch (ProxyFactoryException e)
-        {
-        }
-    }
-
-    public void testInterceptorWithSuperclass()
-    {
-        final Echo echo = (Echo) factory
-                .createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), new Class[]{Echo.class, EchoImpl.class});
-        assertTrue(echo instanceof EchoImpl);
-    }
-
-    public void testInvocationHandlerWithMultipleSuperclasses()
-    {
-        try
-        {
-            factory.createInvokerProxy(new NullInvoker(),
-                    new Class[]{EchoImpl.class, String.class});
-            fail();
-        }
-        catch (ProxyFactoryException e)
-        {
-        }
-    }
-
-    public void testInvokerWithSuperclass()
-    {
-        final Echo echo = (Echo) factory
-                .createInvokerProxy(new NullInvoker(), new Class[]{Echo.class, EchoImpl.class});
-        assertTrue(echo instanceof EchoImpl);
-    }
-
-    public void testProxiesWithClashingFinalMethodInSuperclass()
-    {
-        final Class[] proxyClasses = new Class[]{Echo.class, FinalMethodEcho.class};
-        Echo proxy = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), proxyClasses);
-        assertEquals("final", proxy.echoBack("echo"));
-
-        proxy = (Echo) factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), proxyClasses);
-        assertEquals("final", proxy.echoBack("echo"));
-
-        proxy = (Echo) factory.createInvokerProxy(new NullInvoker(), proxyClasses);
-        assertEquals("final", proxy.echoBack("echo"));
-    }
-
-    public void testWithAbstractSuperclass()
-    {
-        final Echo echo = (Echo) factory.createDelegatorProxy(new ConstantProvider(new EchoImpl()), new Class[]{AbstractEcho.class});
-        assertEquals("hello", echo.echoBack("hello"));
-        assertEquals("helloworld", echo.echoBack("hello", "world"));
-    }
-
-    public void testInterceptorEquals()
-    {
-        final EqualsEcho echo = new EqualsEcho("text");
-        final Echo proxy1 = (Echo) factory.createInterceptorProxy(echo,
-                new NoOpMethodInterceptor(), new Class[] { EqualsEcho.class } );
-        final Echo proxy2 = (Echo) factory.createInterceptorProxy(echo,
-                new NoOpMethodInterceptor(), new Class[] { EqualsEcho.class } );
-        assertEquals(proxy1, proxy1);
-        assertFalse(proxy1.equals(proxy2));
-        assertFalse(proxy2.equals(proxy1));
-    }
-
-    public void testInvokerEquals() throws Exception
-    {
-        final Date proxy1 = (Date) factory.createInvokerProxy(new InvokerTester(), DATE_ONLY);
-        final Date proxy2 = (Date) factory.createInvokerProxy(new InvokerTester(), DATE_ONLY);
-        assertEquals(proxy1, proxy1);
-        assertFalse(proxy1.equals(proxy2));
-        assertFalse(proxy2.equals(proxy1));
-    }
-
-    public void testDelegatorEquals() throws Exception
-    {
-        final EqualsEcho echo = new EqualsEcho("text");
-        final Echo proxy1 = (Echo) factory.createDelegatorProxy(new ConstantProvider(echo),
-                new Class[] { EqualsEcho.class });
-        final Echo proxy2 = (Echo) factory.createDelegatorProxy(new ConstantProvider(echo),
-                new Class[] { EqualsEcho.class });
-        assertEquals(proxy1, proxy1);
-        assertFalse(proxy1.equals(proxy2));
-        assertFalse(proxy2.equals(proxy1));
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    public static final class FinalEcho extends EchoImpl
-    {
-    }
-
-    public static class FinalMethodEcho extends EchoImpl
-    {
-        public final String echoBack(String message)
-        {
-            return "final";
-        }
-    }
-
-    public static class EqualsEcho extends EchoImpl
-    {
-        private final String text;
-
-        public EqualsEcho()
-        {
-            this("testing");
-        }
-
-        public EqualsEcho(String text)
-        {
-            this.text = text;
-        }
-    }
-    
-    private static class InvisibleEcho extends EchoImpl
-    {
-    }
-
-    public static class NoDefaultConstructorEcho extends EchoImpl
-    {
-        public NoDefaultConstructorEcho(String param)
-        {
-        }
-    }
-
-    public static class ProtectedConstructorEcho extends EchoImpl
-    {
-        protected ProtectedConstructorEcho()
-        {
-        }
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/factory/cglib/TestCglibProxyFactory.java b/src/test/java/org/apache/commons/proxy/factory/cglib/TestCglibProxyFactory.java
deleted file mode 100644
index 1bb2f57..0000000
--- a/src/test/java/org/apache/commons/proxy/factory/cglib/TestCglibProxyFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.cglib;
-
-import org.apache.commons.proxy.factory.AbstractSubclassingProxyFactoryTestCase;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class TestCglibProxyFactory extends AbstractSubclassingProxyFactoryTestCase
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public TestCglibProxyFactory()
-    {
-        super(new CglibProxyFactory());
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java b/src/test/java/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java
deleted file mode 100644
index 031221f..0000000
--- a/src/test/java/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.javassist;
-
-import org.apache.commons.proxy.factory.AbstractSubclassingProxyFactoryTestCase;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class TestJavassistProxyFactory extends AbstractSubclassingProxyFactoryTestCase
-{
-//**********************************************************************************************************************
-// Constructors
-//**********************************************************************************************************************
-
-    public TestJavassistProxyFactory()
-    {
-        super(new JavassistProxyFactory());
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/factory/util/TestMethodSignature.java b/src/test/java/org/apache/commons/proxy/factory/util/TestMethodSignature.java
deleted file mode 100644
index 30c5ea4..0000000
--- a/src/test/java/org/apache/commons/proxy/factory/util/TestMethodSignature.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.factory.util;
-
-import junit.framework.TestCase;
-import org.apache.commons.proxy.util.DuplicateEcho;
-import org.apache.commons.proxy.util.Echo;
-
-public class TestMethodSignature extends TestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testEquals() throws Exception
-    {
-        final MethodSignature sig = new MethodSignature(Echo.class.getMethod("echoBack", new Class[] {String.class}));
-        assertTrue(sig.equals(sig));
-        assertFalse(sig.equals("echoBack"));
-        assertEquals(sig, new MethodSignature(Echo.class.getMethod("echoBack", new Class[] {String.class})));
-        assertEquals(sig, new MethodSignature(DuplicateEcho.class.getMethod("echoBack", new Class[] {String.class})));
-        assertFalse(sig.equals(new MethodSignature(Echo.class.getMethod("echoBack", new Class[] {String.class, String.class}))));
-        assertFalse(sig.equals(new MethodSignature(Echo.class.getMethod("echo", new Class[] {}))));
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/TestExecutorInterceptor.java b/src/test/java/org/apache/commons/proxy/interceptor/TestExecutorInterceptor.java
deleted file mode 100644
index f927fe0..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/TestExecutorInterceptor.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import EDU.oswego.cs.dl.util.concurrent.CountDown;
-import EDU.oswego.cs.dl.util.concurrent.Executor;
-import junit.framework.TestCase;
-import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-
-public class TestExecutorInterceptor extends TestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testMethodThrowsException()
-    {
-        final ExceptionEcho impl = new ExceptionEcho();
-        final OneShotExecutor executor = new OneShotExecutor();
-        final Echo proxy = ( Echo ) new CglibProxyFactory()
-                .createInterceptorProxy(impl, new ExecutorInterceptor(executor), new Class[] {Echo.class});
-        proxy.echo();
-    }
-
-    public void testNonVoidMethod() throws Exception
-    {
-        final ExecutedEcho impl = new ExecutedEcho();
-        final OneShotExecutor executor = new OneShotExecutor();
-        final Echo proxy = ( Echo ) new CglibProxyFactory()
-                .createInterceptorProxy(impl, new ExecutorInterceptor(executor), new Class[] {Echo.class});
-        try
-        {
-            proxy.echoBack("hello");
-            fail();
-        }
-        catch( IllegalArgumentException e )
-        {
-        }
-    }
-
-    public void testVoidMethod() throws Exception
-    {
-        final ExecutedEcho impl = new ExecutedEcho();
-        final OneShotExecutor executor = new OneShotExecutor();
-        final Echo proxy = ( Echo ) new CglibProxyFactory()
-                .createInterceptorProxy(impl, new ExecutorInterceptor(executor), new Class[] {Echo.class});
-        proxy.echo();
-        executor.getLatch().acquire();
-        assertEquals(executor.getThread(), impl.getExecutionThread());
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-//
-// Inner Classes
-//
-    public static class ExceptionEcho extends EchoImpl
-    {
-        public void echo()
-        {
-            throw new RuntimeException("Oops!");
-        }
-    }
-
-    public static class ExecutedEcho extends EchoImpl
-    {
-        private Thread executionThread;
-
-        public void echo()
-        {
-            executionThread = Thread.currentThread();
-        }
-
-        public Thread getExecutionThread()
-        {
-            return executionThread;
-        }
-    }
-
-    private static class OneShotExecutor implements Executor
-    {
-        private Thread thread;
-        private CountDown latch = new CountDown(1);
-
-        public void execute( final Runnable command )
-        {
-            thread = new Thread(new Runnable()
-            {
-                public void run()
-                {
-                    try
-                    {
-                        command.run();
-                    }
-                    finally
-                    {
-                        latch.release();
-                    }
-                }
-            });
-            thread.start();
-        }
-
-        public Thread getThread()
-        {
-            return thread;
-        }
-
-        public CountDown getLatch()
-        {
-            return latch;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/TestFilteredInterceptor.java b/src/test/java/org/apache/commons/proxy/interceptor/TestFilteredInterceptor.java
deleted file mode 100644
index 403d19a..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/TestFilteredInterceptor.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
-import org.apache.commons.proxy.interceptor.filter.SimpleFilter;
-import org.apache.commons.proxy.util.AbstractTestCase;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-import org.apache.commons.proxy.util.SuffixInterceptor;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class TestFilteredInterceptor extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testSerialization()
-    {
-        assertSerializable(new FilteredInterceptor(new SuffixInterceptor("a"), new SimpleFilter(new String[] {"echoBack"})));
-    }
-    
-    public void testFilterAccepts()
-    {
-        Echo echo = ( Echo ) new InterceptorChain(new Interceptor[] {new FilteredInterceptor(new SuffixInterceptor("a"), new SimpleFilter(new String[] {"echoBack"}))}).createProxyProvider(new CglibProxyFactory(), new EchoImpl()).getObject();
-        assertEquals("messagea", echo.echoBack("message"));
-    }
-
-    public void testFilterDenies()
-    {
-        Echo echo = ( Echo ) new InterceptorChain(new Interceptor[] {new FilteredInterceptor(new SuffixInterceptor("a"), new SimpleFilter())}).createProxyProvider(new CglibProxyFactory(), new EchoImpl()).getObject();
-        assertEquals("message", echo.echoBack("message"));
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/TestInterceptorChain.java b/src/test/java/org/apache/commons/proxy/interceptor/TestInterceptorChain.java
deleted file mode 100644
index daea4db..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/TestInterceptorChain.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.proxy.Interceptor;
-import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-import org.apache.commons.proxy.util.SuffixInterceptor;
-import org.apache.commons.proxy.util.AbstractTestCase;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class TestInterceptorChain extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testSerializable()
-    {
-        Echo echo = ( Echo ) new InterceptorChain(new Interceptor[] {new SuffixInterceptor("a"), new SuffixInterceptor("b")}).createProxyProvider(new CglibProxyFactory(), new EchoImpl(), new Class[] {Echo.class}).getObject();
-        assertSerializable(echo);
-    }
-    
-    public void testWithMultipleInterceptors()
-    {
-        Echo echo = ( Echo ) new InterceptorChain(new Interceptor[] {new SuffixInterceptor("a"), new SuffixInterceptor("b")}).createProxyProvider(new CglibProxyFactory(), new EchoImpl(), new Class[] {Echo.class}).getObject();
-        assertEquals("messageba", echo.echoBack("message"));
-    }
-
-    public void testWithSingleInterceptor()
-    {
-        Echo echo = ( Echo ) new InterceptorChain(new Interceptor[] {new SuffixInterceptor("a")}).createProxyProvider(new CglibProxyFactory(), new EchoImpl(), new Class[] {Echo.class}).getObject();
-        assertEquals("messagea", echo.echoBack("message"));
-    }
-}
-
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/TestLoggingInterceptor.java b/src/test/java/org/apache/commons/proxy/interceptor/TestLoggingInterceptor.java
deleted file mode 100644
index 8b3feda..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/TestLoggingInterceptor.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-import org.jmock.Mock;
-import org.jmock.MockObjectTestCase;
-
-import java.io.IOException;
-
-public class TestLoggingInterceptor extends MockObjectTestCase
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private Mock logMock;
-    private Echo echo;
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    protected void setUp() throws Exception
-    {
-        logMock = mock(Log.class);
-        echo = ( Echo ) new CglibProxyFactory()
-                .createInterceptorProxy(new EchoImpl(), new LoggingInterceptor(( Log ) logMock.proxy()),
-                        new Class[] {Echo.class});
-    }
-
-    public void testException()
-    {
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(true));
-        logMock.expects(once()).method("debug").with(eq("BEGIN ioException()"));
-        logMock.expects(once()).method("debug").with(eq("EXCEPTION ioException() -- java.io.IOException"), isA(IOException.class));
-        try
-        {
-            echo.ioException();
-            fail();
-        }
-        catch( IOException e )
-        {
-        }
-    }
-
-    public void testMultipleParameters()
-    {
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(true));
-        logMock.expects(once()).method("debug").with(eq("BEGIN echoBack(Hello, World)"));
-        logMock.expects(once()).method("debug").with(eq("END echoBack() [HelloWorld]"));
-        echo.echoBack("Hello", "World");
-    }
-
-    public void testNonVoidMethod()
-    {
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(true));
-        logMock.expects(once()).method("debug").with(eq("BEGIN echoBack(Hello)"));
-        logMock.expects(once()).method("debug").with(eq("END echoBack() [Hello]"));
-        echo.echoBack("Hello");
-    }
-
-    public void testNullReturnValue()
-    {
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(true));
-        logMock.expects(once()).method("debug").with(eq("BEGIN echoBack(<null>)"));
-        logMock.expects(once()).method("debug").with(eq("END echoBack() [<null>]"));
-        echo.echoBack(( String ) null);
-    }
-
-    public void testRuntimeException()
-    {
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(true));
-        logMock.expects(once()).method("debug").with(eq("BEGIN illegalArgument()"));
-        logMock.expects(once()).method("debug").with(eq("EXCEPTION illegalArgument() -- java.lang.IllegalArgumentException"), isA(IllegalArgumentException.class));
-        try
-        {
-            echo.illegalArgument();
-            fail();
-        }
-        catch( IllegalArgumentException e )
-        {
-        }
-    }
-
-    public void testVoidMethod()
-    {
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(true));
-        logMock.expects(once()).method("debug").with(eq("BEGIN echo()"));
-        logMock.expects(once()).method("debug").with(eq("END echo()"));
-        echo.echo();
-    }
-
-    public void testWhenLoggingDisabled()
-    {
-        logMock = mock(Log.class);
-        echo = ( Echo ) new CglibProxyFactory()
-                .createInterceptorProxy(new EchoImpl(), new LoggingInterceptor(( Log ) logMock.proxy()),
-                        new Class[] {Echo.class});
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(false));
-        echo.echoBack("Hello");
-    }
-
-    public void testWithArrayParameter()
-    {
-        logMock.expects(once()).method("isDebugEnabled").will(returnValue(true));
-        logMock.expects(once()).method("debug").with(eq("BEGIN echoBack((java.lang.String[]){Hello, World})"));
-        logMock.expects(once()).method("debug").with(eq("END echoBack() [HelloWorld]"));
-        echo.echoBack(new String[] {"Hello", "World"});
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/TestMethodInterceptorAdapter.java b/src/test/java/org/apache/commons/proxy/interceptor/TestMethodInterceptorAdapter.java
deleted file mode 100644
index 108d376..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/TestMethodInterceptorAdapter.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.aopalliance.intercept.MethodInterceptor;
-import org.aopalliance.intercept.MethodInvocation;
-import org.apache.commons.proxy.factory.javassist.JavassistProxyFactory;
-import org.apache.commons.proxy.util.AbstractTestCase;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-
-import java.io.Serializable;
-
-public class TestMethodInterceptorAdapter extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testSerialization()
-    {
-        final Echo proxy = ( Echo ) new JavassistProxyFactory().createInterceptorProxy(new EchoImpl(),
-                new MethodInterceptorAdapter(new SuffixMethodInterceptor(
-                        " suffix")),
-                new Class[] {Echo.class});
-        assertSerializable(proxy);
-    }
-    
-    public void testMethodInterception()
-    {
-        final Echo proxy = ( Echo ) new JavassistProxyFactory().createInterceptorProxy(new EchoImpl(),
-                new MethodInterceptorAdapter(new SuffixMethodInterceptor(
-                        " suffix")),
-                new Class[] {Echo.class});
-        assertEquals("message suffix", proxy.echoBack("message"));
-    }
-
-    public void testMethodInvocationImplementation() throws Exception
-    {
-        final InterceptorTester tester = new InterceptorTester();
-        final EchoImpl target = new EchoImpl();
-        final Echo proxy = ( Echo ) new JavassistProxyFactory().createInterceptorProxy(target, new MethodInterceptorAdapter(tester), new Class[] {Echo.class});
-        proxy.echo();
-        assertNotNull(tester.invocation.getArguments());
-        assertEquals(0, tester.invocation.getArguments().length);
-        assertEquals(Echo.class.getMethod("echo", new Class[] {}), tester.invocation.getMethod());
-        assertEquals(Echo.class.getMethod("echo", new Class[] {}), tester.invocation.getStaticPart());
-        assertEquals(target, tester.invocation.getThis());
-        proxy.echoBack("Hello");
-        assertNotNull(tester.invocation.getArguments());
-        assertEquals(1, tester.invocation.getArguments().length);
-        assertEquals("Hello", tester.invocation.getArguments()[0]);
-        assertEquals(Echo.class.getMethod("echoBack", new Class[] {String.class}), tester.invocation.getMethod());
-        assertEquals(Echo.class.getMethod("echoBack", new Class[] {String.class}), tester.invocation.getStaticPart());
-        proxy.echoBack("Hello", "World");
-        assertNotNull(tester.invocation.getArguments());
-        assertEquals(2, tester.invocation.getArguments().length);
-        assertEquals("Hello", tester.invocation.getArguments()[0]);
-        assertEquals("World", tester.invocation.getArguments()[1]);
-        assertEquals(Echo.class.getMethod("echoBack", new Class[] {String.class, String.class}), tester.invocation.getMethod());
-        assertEquals(Echo.class.getMethod("echoBack", new Class[] {String.class, String.class}), tester.invocation.getStaticPart());
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private static class InterceptorTester implements MethodInterceptor
-    {
-        private MethodInvocation invocation;
-
-        public Object invoke( MethodInvocation methodInvocation ) throws Throwable
-        {
-            this.invocation = methodInvocation;
-            return methodInvocation.proceed();
-        }
-    }
-
-    private static class SuffixMethodInterceptor implements MethodInterceptor, Serializable
-    {
-        private final String suffix;
-
-        public SuffixMethodInterceptor( String suffix )
-        {
-            this.suffix = suffix;
-        }
-
-        public Object invoke( MethodInvocation methodInvocation ) throws Throwable
-        {
-            return methodInvocation.proceed() + suffix;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/TestSerializingInterceptor.java b/src/test/java/org/apache/commons/proxy/interceptor/TestSerializingInterceptor.java
deleted file mode 100644
index cb29b37..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/TestSerializingInterceptor.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor;
-
-import org.apache.commons.proxy.ProxyFactory;
-import org.apache.commons.proxy.util.AbstractTestCase;
-
-import java.io.ByteArrayOutputStream;
-
-public class TestSerializingInterceptor extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testSerialization()
-    {
-        assertSerializable(new SerializingInterceptor());
-    }
-
-    public void testWithInvalidParameterType()
-    {
-        try
-        {
-            final ObjectEchoImpl target = new ObjectEchoImpl();
-            ObjectEcho echo =
-                    ( ObjectEcho ) new ProxyFactory().createInterceptorProxy(target,
-                            new SerializingInterceptor(),
-                            new Class[] {ObjectEcho.class});
-            final Object originalParameter = new ByteArrayOutputStream();
-            echo.echoBack(originalParameter);
-            fail("Should not be able to call method with non-serializable parameter type.");
-        }
-        catch( RuntimeException e )
-        {
-        }
-    }
-
-    public void testWithSerializableParametersAndReturn()
-    {
-        final ObjectEchoImpl target = new ObjectEchoImpl();
-        ObjectEcho echo =
-                ( ObjectEcho ) new ProxyFactory().createInterceptorProxy(target,
-                        new SerializingInterceptor(),
-                        new Class[] {ObjectEcho.class});
-        final Object originalParameter = "Hello, World!";
-        final Object returnValue = echo.echoBack(originalParameter);
-        assertNotSame(originalParameter, target.parameter);
-        assertNotSame(originalParameter, returnValue);
-        assertNotSame(returnValue, target.parameter);
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    public static interface ObjectEcho
-    {
-        public Object echoBack( Object object );
-    }
-
-    public static class ObjectEchoImpl implements ObjectEcho
-    {
-        private Object parameter;
-
-        public Object echoBack( Object object )
-        {
-            this.parameter = object;
-            return object;
-        }
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/filter/TestPatternFilter.java b/src/test/java/org/apache/commons/proxy/interceptor/filter/TestPatternFilter.java
deleted file mode 100644
index 165abbe..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/filter/TestPatternFilter.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.filter;
-
-import junit.framework.TestCase;
-import org.apache.commons.proxy.interceptor.MethodFilter;
-
-import java.util.Date;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class TestPatternFilter extends TestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testAccepts() throws Exception
-    {
-        final MethodFilter filter = PatternFilter.getterSetterFilter();
-        assertTrue(filter.accepts(Date.class.getMethod("getSeconds", new Class[] {})));
-        assertTrue(filter.accepts(Date.class.getMethod("getMinutes", new Class[] {})));
-        assertTrue(filter.accepts(Date.class.getMethod("setSeconds", new Class[] {Integer.TYPE})));
-        assertTrue(filter.accepts(Date.class.getMethod("setMinutes", new Class[] {Integer.TYPE})));
-        assertFalse(filter.accepts(Date.class.getMethod("toString", new Class[] {})));
-        assertFalse(filter.accepts(Date.class.getMethod("hashCode", new Class[] {})));
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/filter/TestReturnTypeFilter.java b/src/test/java/org/apache/commons/proxy/interceptor/filter/TestReturnTypeFilter.java
deleted file mode 100644
index 6ab7862..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/filter/TestReturnTypeFilter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.filter;
-
-import junit.framework.TestCase;
-
-public class TestReturnTypeFilter extends TestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testAcceptsMethod() throws Exception
-    {
-        final ReturnTypeFilter filter = new ReturnTypeFilter(new Class[] {String.class, Integer.TYPE});
-        assertTrue(filter.accepts(Object.class.getMethod("toString", new Class[] {})));
-        assertTrue(filter.accepts(Object.class.getMethod("hashCode", new Class[] {})));
-        assertFalse(filter.accepts(Object.class.getMethod("equals", new Class[] {Object.class})));
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/interceptor/filter/TestSimpleFilter.java b/src/test/java/org/apache/commons/proxy/interceptor/filter/TestSimpleFilter.java
deleted file mode 100644
index 366393d..0000000
--- a/src/test/java/org/apache/commons/proxy/interceptor/filter/TestSimpleFilter.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.interceptor.filter;
-
-import junit.framework.TestCase;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-
-/**
- * @author James Carman
- * @since 1.0
- */
-public class TestSimpleFilter extends TestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testAccepts() throws Exception
-    {
-        final SimpleFilter filter = new SimpleFilter(new String[] {"echoBack"});
-        assertTrue(filter.accepts(Echo.class.getMethod("echoBack", new Class[] {String.class})));
-        assertFalse(filter.accepts(EchoImpl.class.getMethod("hashCode", new Class[] {})));
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/invoker/TestChainInvoker.java b/src/test/java/org/apache/commons/proxy/invoker/TestChainInvoker.java
deleted file mode 100644
index 9daa136..0000000
--- a/src/test/java/org/apache/commons/proxy/invoker/TestChainInvoker.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.util.AbstractTestCase;
-import org.apache.commons.proxy.ProxyFactory;
-
-/**
- * @since 1.1
- */
-public class TestChainInvoker extends AbstractTestCase
-{
-    public void testSkipDefaultValues()
-    {
-        final ChainInvoker invoker = new ChainInvoker(new Object[]{new DefaultTester(), new NonDefaultTester()});
-        Tester tester = (Tester)new ProxyFactory().createInvokerProxy(invoker, new Class[] { Tester.class });
-        assertEquals(true,tester.booleanMethod());
-        assertEquals(1, tester.byteMethod());
-        assertEquals('1', tester.charMethod());
-        assertEquals(1.0, tester.doubleMethod(), 0.0);
-        assertEquals(1.0f, tester.floatMethod(), 0.0f);
-        assertEquals(1, tester.intMethod());
-        assertEquals(1, tester.longMethod());
-        assertEquals(1, tester.shortMethod());
-        assertEquals("One", tester.objectMethod());
-    }
-
-    public void testReturnDefaultValue()
-    {
-        final ChainInvoker invoker = new ChainInvoker(new Object[]{new DefaultTester(), new DefaultTester()});
-        Tester tester = (Tester)new ProxyFactory().createInvokerProxy(invoker, new Class[] { Tester.class });
-        assertEquals(false,tester.booleanMethod());
-        assertEquals(0, tester.byteMethod());
-        assertEquals(0, tester.charMethod());
-        assertEquals(0.0, tester.doubleMethod(), 0.0);
-        assertEquals(0.0f, tester.floatMethod(), 0.0f);
-        assertEquals(0, tester.intMethod());
-        assertEquals(0, tester.longMethod());
-        assertEquals(0, tester.shortMethod());
-        assertEquals(null, tester.objectMethod());
-    }
-
-    public static interface Tester
-    {
-        public int intMethod();
-
-        public long longMethod();
-
-        public short shortMethod();
-
-        public byte byteMethod();
-
-        public double doubleMethod();
-
-        public float floatMethod();
-
-        public boolean booleanMethod();
-
-        public char charMethod();
-
-        public Object objectMethod();
-    }
-
-    public class NonDefaultTester implements Tester
-    {
-        public boolean booleanMethod()
-        {
-            return true;
-        }
-
-        public byte byteMethod()
-        {
-            return 1;
-        }
-
-        public char charMethod()
-        {
-            return '1';
-        }
-
-        public double doubleMethod()
-        {
-            return 1.0;
-        }
-
-        public float floatMethod()
-        {
-            return 1.0f;
-        }
-
-        public int intMethod()
-        {
-            return 1;
-        }
-
-        public long longMethod()
-        {
-            return 1;
-        }
-
-        public Object objectMethod()
-        {
-            return "One";
-        }
-
-        public short shortMethod()
-        {
-            return 1;
-        }
-    }
-
-    public class DefaultTester implements Tester
-    {
-        public boolean booleanMethod()
-        {
-            return false;
-        }
-
-        public byte byteMethod()
-        {
-            return 0;
-        }
-
-        public char charMethod()
-        {
-            return 0;
-        }
-
-        public double doubleMethod()
-        {
-            return 0;
-        }
-
-        public float floatMethod()
-        {
-            return 0;
-        }
-
-        public int intMethod()
-        {
-            return 0;
-        }
-
-        public long longMethod()
-        {
-            return 0;
-        }
-
-        public Object objectMethod()
-        {
-            return null;
-        }
-
-        public short shortMethod()
-        {
-            return 0;
-        }
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/invoker/TestDuckTypingInvoker.java b/src/test/java/org/apache/commons/proxy/invoker/TestDuckTypingInvoker.java
deleted file mode 100644
index 353e62d..0000000
--- a/src/test/java/org/apache/commons/proxy/invoker/TestDuckTypingInvoker.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.ObjectProvider;
-import org.apache.commons.proxy.ProxyFactory;
-import org.apache.commons.proxy.provider.ConstantProvider;
-import org.apache.commons.proxy.util.AbstractTestCase;
-
-import java.io.Serializable;
-
-/**
- *
- */
-public class TestDuckTypingInvoker extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testExactSignatureMatch()
-    {
-        final ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck());
-        final DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
-        final Duck duck = ( Duck ) new ProxyFactory().createInvokerProxy(invoker, new Class[] {Duck.class});
-        assertEquals("Quack!", duck.sayQuack());
-    }
-
-    public void testMismatchingParameterType()
-    {
-        final ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck());
-        final DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
-        final ParameterizedDuck parameterizedDuck = ( ParameterizedDuck ) new ProxyFactory()
-                .createInvokerProxy(invoker, new Class[] {ParameterizedDuck.class});
-        try
-        {
-            parameterizedDuck.sayQuack("Elmer");
-            fail("No matching method should be found.");
-        }
-        catch( UnsupportedOperationException e )
-        {
-            // Do nothing, expected behavior!
-        }
-    }
-
-    public void testMismatchingReturnType()
-    {
-        final ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck());
-        final DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
-        final VoidReturnDuck voidDuck = ( VoidReturnDuck ) new ProxyFactory().createInvokerProxy(invoker, new Class[] {
-                VoidReturnDuck.class});
-        try
-        {
-            voidDuck.sayQuack();
-            fail("No matching method should be found.");
-        }
-        catch( UnsupportedOperationException e )
-        {
-            // Do nothing, expected behavior!
-        }
-    }
-
-    public void testNoMatchingMethod()
-    {
-        final ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck());
-        final DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
-        final Goose goose = ( Goose ) new ProxyFactory().createInvokerProxy(invoker, new Class[] {Goose.class});
-        try
-        {
-            goose.sayHonk();
-            fail("No matching method should be found.");
-        }
-        catch( UnsupportedOperationException e )
-        {
-            // Do nothing, expected behavior!
-        }
-    }
-
-    public void testSerialization()
-    {
-        final ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck());
-        final DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
-        assertSerializable(invoker);
-    }
-
-    public void testTargetHasCompatibleReturnType()
-    {
-        final ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck());
-        final DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
-        final SerializableDuck duck = ( SerializableDuck ) new ProxyFactory().createInvokerProxy(invoker, new Class[] {
-                SerializableDuck.class});
-        assertEquals("Quack!", duck.sayQuack());
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    public interface Duck
-    {
-        public String sayQuack();
-    }
-
-    public interface Goose
-    {
-        public void sayHonk();
-    }
-
-    public static class LegacyDuck implements Serializable
-    {
-        public String sayQuack()
-        {
-            return "Quack!";
-        }
-    }
-
-    public interface ParameterizedDuck
-    {
-        public String sayQuack( String recipient );
-    }
-
-    public interface SerializableDuck
-    {
-        public Serializable sayQuack();
-    }
-
-    public interface VoidReturnDuck
-    {
-        public void sayQuack();
-    }
-}
diff --git a/src/test/java/org/apache/commons/proxy/invoker/TestInvocationHandlerAdapter.java b/src/test/java/org/apache/commons/proxy/invoker/TestInvocationHandlerAdapter.java
deleted file mode 100644
index ebd337c..0000000
--- a/src/test/java/org/apache/commons/proxy/invoker/TestInvocationHandlerAdapter.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.factory.javassist.JavassistProxyFactory;
-import org.apache.commons.proxy.util.AbstractTestCase;
-import org.apache.commons.proxy.util.Echo;
-
-import java.io.Serializable;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-
-public class TestInvocationHandlerAdapter extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testSerialization()
-    {
-        assertSerializable( new InvocationHandlerAdapter( new InvocationHandlerTester() ) );
-    }
-    
-    public void testMethodInvocation() throws Exception
-    {
-        InvocationHandlerTester tester = new InvocationHandlerTester();
-        final Echo echo = ( Echo ) new JavassistProxyFactory().createInvokerProxy(new InvocationHandlerAdapter(tester), new Class[] {Echo.class});
-        echo.echoBack("hello");
-        assertEquals(Echo.class.getMethod("echoBack", new Class[] {String.class}), tester.method);
-        assertSame(echo, tester.proxy);
-        assertNotNull(tester.arguments);
-        assertEquals(1, tester.arguments.length);
-        assertEquals("hello", tester.arguments[0]);
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    private static class InvocationHandlerTester implements InvocationHandler, Serializable
-    {
-        private Object proxy;
-        private Method method;
-        private Object[] arguments;
-
-        public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
-        {
-            this.proxy = proxy;
-            this.method = method;
-            this.arguments = args;
-            return null;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/invoker/TestNullInvoker.java b/src/test/java/org/apache/commons/proxy/invoker/TestNullInvoker.java
deleted file mode 100644
index 0b9c6f9..0000000
--- a/src/test/java/org/apache/commons/proxy/invoker/TestNullInvoker.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import org.apache.commons.proxy.ProxyUtils;
-import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
-import org.apache.commons.proxy.util.AbstractTestCase;
-
-public class TestNullInvoker extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testReturnValues()
-    {
-        final Tester tester = ( Tester ) ProxyUtils.createNullObject(new CglibProxyFactory(), new Class[] {Tester.class});
-        assertEquals(0, tester.intMethod());
-        assertEquals(0L, tester.longMethod());
-        assertEquals(( short ) 0, tester.shortMethod());
-        assertEquals(( byte ) 0, tester.byteMethod());
-        assertEquals(( char ) 0, tester.charMethod());
-        assertEquals(0.0f, tester.floatMethod(), 0.0f);
-        assertEquals(0.0, tester.doubleMethod(), 0.0f);
-        assertFalse(tester.booleanMethod());
-        assertNull(tester.stringMethod());
-    }
-
-    public void testSerialization()
-    {
-        assertSerializable(new NullInvoker());
-    }
-
-//**********************************************************************************************************************
-// Inner Classes
-//**********************************************************************************************************************
-
-    public static interface Tester
-    {
-        public int intMethod();
-
-        public long longMethod();
-
-        public short shortMethod();
-
-        public byte byteMethod();
-
-        public char charMethod();
-
-        public double doubleMethod();
-
-        public float floatMethod();
-
-        public String stringMethod();
-
-        public boolean booleanMethod();
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/invoker/TestXmlRpcInvoker.java b/src/test/java/org/apache/commons/proxy/invoker/TestXmlRpcInvoker.java
deleted file mode 100644
index 5a2cc56..0000000
--- a/src/test/java/org/apache/commons/proxy/invoker/TestXmlRpcInvoker.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.invoker;
-
-import junit.extensions.TestSetup;
-import junit.framework.Protectable;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestResult;
-import junit.framework.TestSuite;
-import org.apache.commons.proxy.exception.InvokerException;
-import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
-import org.apache.commons.proxy.util.Echo;
-import org.apache.commons.proxy.util.EchoImpl;
-import org.apache.xmlrpc.WebServer;
-import org.apache.xmlrpc.XmlRpcClient;
-import org.apache.xmlrpc.XmlRpcClientLite;
-
-/**
- * @author James Carman
- */
-public class TestXmlRpcInvoker extends TestCase
-{
-//**********************************************************************************************************************
-// Fields
-//**********************************************************************************************************************
-
-    private static WebServer server;
-    private static XmlRpcClient client;
-
-//**********************************************************************************************************************
-// Static Methods
-//**********************************************************************************************************************
-
-    public static Test suite()
-    {
-        return new TestSetup(new TestSuite(TestXmlRpcInvoker.class))
-        {
-            public void run( final TestResult testResult )
-            {
-                Protectable p = new Protectable()
-                {
-                    public void protect() throws Throwable
-                    {
-                        try
-                        {
-                            setUp();
-                            basicRun(testResult);
-                        }
-                        finally
-                        {
-                            tearDown();
-                        }
-                    }
-                };
-                testResult.runProtected(this, p);
-            }
-
-            protected void setUp() throws Exception
-            {
-                server = new WebServer(9999);
-                server.addHandler("echo", new EchoImpl());
-                server.start();
-                client = new XmlRpcClientLite("http://localhost:9999/RPC2");
-            }
-
-            protected void tearDown() throws Exception
-            {
-                server.shutdown();
-            }
-        };
-    }
-
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testInvalidHandlerName()
-    {
-        final XmlRpcInvoker handler = new XmlRpcInvoker(client, "invalid");
-        final Echo echo = ( Echo ) new CglibProxyFactory()
-                .createInvokerProxy(handler, new Class[] {Echo.class});
-        try
-        {
-            echo.echoBack("Hello");
-            fail();
-        }
-        catch( InvokerException e )
-        {
-        }
-    }
-
-    public void testValidInvocation() throws Exception
-    {
-        final XmlRpcInvoker handler = new XmlRpcInvoker(client, "echo");
-        final Echo echo = ( Echo ) new CglibProxyFactory()
-                .createInvokerProxy(handler, new Class[] {Echo.class});
-        assertEquals("Hello", echo.echoBack("Hello"));
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/provider/remoting/TestBurlapProvider.java b/src/test/java/org/apache/commons/proxy/provider/remoting/TestBurlapProvider.java
deleted file mode 100644
index f3dcc3a..0000000
--- a/src/test/java/org/apache/commons/proxy/provider/remoting/TestBurlapProvider.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider.remoting;
-
-import org.apache.commons.proxy.exception.ObjectProviderException;
-import org.apache.commons.proxy.util.AbstractTestCase;
-import org.apache.commons.proxy.util.Echo;
-
-public class TestBurlapProvider extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testSerialization()
-    {
-        final BurlapProvider p = new BurlapProvider();
-        p.setServiceInterface(Echo.class);
-        p.setUrl("a malformed URL");
-        assertSerializable(p);
-    }
-
-    public void testWithMalformedUrl()
-    {
-        try
-        {
-            final BurlapProvider p = new BurlapProvider(Echo.class, "a malformed URL");
-            p.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
-    }
-
-    public void testWithMalformedUrlBean()
-    {
-        try
-        {
-            final BurlapProvider p = new BurlapProvider();
-            p.setServiceInterface(Echo.class);
-            p.setUrl("a malformed URL");
-            p.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/provider/remoting/TestHessianProvider.java b/src/test/java/org/apache/commons/proxy/provider/remoting/TestHessianProvider.java
deleted file mode 100644
index e002b90..0000000
--- a/src/test/java/org/apache/commons/proxy/provider/remoting/TestHessianProvider.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.commons.proxy.provider.remoting;
-
-import org.apache.commons.proxy.exception.ObjectProviderException;
-import org.apache.commons.proxy.util.AbstractTestCase;
-import org.apache.commons.proxy.util.Echo;
-
-public class TestHessianProvider extends AbstractTestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    public void testSerialization()
-    {
-        final HessianProvider p = new HessianProvider();
-        p.setServiceInterface(Echo.class);
-        p.setUrl("a malformed URL");
-        assertSerializable(p);
-    }
-
-    public void testWithMalformedUrl()
-    {
-        try
-        {
-            final HessianProvider p = new HessianProvider(Echo.class, "a malformed URL");
-            p.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
-    }
-
-    public void testWithMalformedUrlBean()
-    {
-        try
-        {
-            final HessianProvider p = new HessianProvider();
-            p.setServiceInterface(Echo.class);
-            p.setUrl("a malformed URL");
-            p.getObject();
-            fail();
-        }
-        catch( ObjectProviderException e )
-        {
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/proxy/util/AbstractTestCase.java b/src/test/java/org/apache/commons/proxy/util/AbstractTestCase.java
deleted file mode 100644
index 430c4fd..0000000
--- a/src/test/java/org/apache/commons/proxy/util/AbstractTestCase.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.apache.commons.proxy.util;
-
-import junit.framework.TestCase;
-import org.apache.commons.lang.SerializationUtils;
-
-import java.io.Serializable;
-
-/**
- * @author James Carman
- * @since 1.1
- */
-public abstract class AbstractTestCase extends TestCase
-{
-//**********************************************************************************************************************
-// Other Methods
-//**********************************************************************************************************************
-
-    protected void assertSerializable( Object o )
-    {
-        assertTrue(o instanceof Serializable);
-        SerializationUtils.clone(( Serializable ) o);
-    }
-}
diff --git a/test/pom.xml b/test/pom.xml
new file mode 100644
index 0000000..7f42ec8
--- /dev/null
+++ b/test/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <!--
+        ~ Licensed to the Apache Software Foundation (ASF) under one or more ~
+        contributor license agreements. See the NOTICE file distributed with ~
+        this work for additional information regarding copyright ownership. ~
+        The ASF licenses this file to You 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.
+    -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>commons-proxy2-parent</artifactId>
+        <groupId>org.apache.commons</groupId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>commons-proxy2-test</artifactId>
+    <name>Commons Proxy Test</name>
+    <description>Tests things that depend on multiple modules</description>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2-jdk</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2-cglib</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <version>2.1_3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>commons-proxy2-javassist</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/test/src/main/resources/dummy b/test/src/main/resources/dummy
new file mode 100644
index 0000000..421376d
--- /dev/null
+++ b/test/src/main/resources/dummy
@@ -0,0 +1 @@
+dummy
diff --git a/test/src/test/java/org/apache/commons/proxy2/DefaultProxyFactoryTest.java b/test/src/test/java/org/apache/commons/proxy2/DefaultProxyFactoryTest.java
new file mode 100644
index 0000000..a5657e0
--- /dev/null
+++ b/test/src/test/java/org/apache/commons/proxy2/DefaultProxyFactoryTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2;
+
+import org.apache.commons.proxy2.invoker.NullInvoker;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Proxy;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test the default ProxyFactory provided by {@link ProxyUtils}.
+ */
+public class DefaultProxyFactoryTest {
+    private ProxyFactory proxyFactory;
+
+    @Before
+    public void setUp() {
+        proxyFactory = ProxyUtils.proxyFactory();
+    }
+
+    @Test
+    public void testBasic() {
+        Foo foo = proxyFactory.createInvokerProxy(NullInvoker.INSTANCE,
+                Foo.class);
+        assertNotNull(foo);
+        assertTrue(foo instanceof Proxy);
+    }
+
+    @Test
+    public void testSubclassing() {
+        Bar bar = proxyFactory.createInvokerProxy(NullInvoker.INSTANCE,
+                Bar.class);
+        assertNotNull(bar);
+    }
+
+    @Test
+    public void testCombined() {
+        Bar bar = proxyFactory.createInvokerProxy(NullInvoker.INSTANCE,
+                Bar.class, Foo.class);
+        assertNotNull(bar);
+        assertTrue(bar instanceof Foo);
+    }
+
+    public interface Foo {
+    }
+
+    public static class Bar {
+    }
+}
diff --git a/test/src/test/java/org/apache/commons/proxy2/stub/AbstractStubTestCase.java b/test/src/test/java/org/apache/commons/proxy2/stub/AbstractStubTestCase.java
new file mode 100644
index 0000000..534a26d
--- /dev/null
+++ b/test/src/test/java/org/apache/commons/proxy2/stub/AbstractStubTestCase.java
@@ -0,0 +1,441 @@
+package org.apache.commons.proxy2.stub;
+
+import org.apache.commons.proxy2.ProxyFactory;
+import org.apache.commons.proxy2.cglib.CglibProxyFactory;
+import org.apache.commons.proxy2.invoker.NullInvoker;
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public abstract class AbstractStubTestCase
+{
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Fields
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    protected ProxyFactory proxyFactory;
+    protected StubInterface target;
+
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Abstract Methods
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    protected abstract StubInterface createProxy(Trainer<StubInterface> trainer);
+
+    // ----------------------------------------------------------------------------------------------------------------------
+    // Other Methods
+    // ----------------------------------------------------------------------------------------------------------------------
+
+    @Before
+    public final void setUpProxyFactory()
+    {
+        this.proxyFactory = new CglibProxyFactory();
+        this.target = proxyFactory.createInvokerProxy(NullInvoker.INSTANCE, StubInterface.class);
+    }
+
+    @Test
+    public void testAnyMatcher()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one(any(String.class))).thenReturn("World");
+            }
+        });
+        assertEquals("World", proxy.one("Hello"));
+        assertEquals("World", proxy.one(null));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testMixingArgumentMatchingStrategies()
+    {
+        createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.three(isInstance(String.class), "World"))
+                        .thenAnswer(ObjectProviderUtils.constant("World"));
+            }
+        });
+    }
+
+    @Test
+    public void testStubReturn()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.stub()).thenStub(new Trainer<StubInterface>()
+                {
+                    @Override
+                    protected void train(StubInterface trainee)
+                    {
+                        when(trainee.one("Hello")).thenReturn("World");
+                    }
+                });
+            }
+        });
+        assertNotNull(proxy.stub());
+        assertEquals("World", proxy.stub().one("Hello"));
+    }
+
+    @Test
+    public void testStubArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.stubs()).thenBuildArray().addElement(new Trainer<StubInterface>()
+                {
+                    @Override
+                    protected void train(StubInterface trainee)
+                    {
+                        when(trainee.one("Whatever")).thenReturn("Zero");
+                    }
+                }).addElement(new Trainer<StubInterface>()
+                {
+                    @Override
+                    protected void train(StubInterface trainee)
+                    {
+                        when(trainee.one("Whatever")).thenReturn("One");
+                    }
+                })
+                .build();
+            }
+        });
+
+        assertEquals("Zero", proxy.stubs()[0].one("Whatever"));
+        assertEquals("One", proxy.stubs()[1].one("Whatever"));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testThenBeforeWhen()
+    {
+        createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                thenThrow(new RuntimeException("Oops!"));
+            }
+        });
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testThrowExceptionWithException()
+    {
+        StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                trainee.voidMethod("Hello");
+                thenThrow(new IllegalArgumentException("Nope!"));
+            }
+        });
+        proxy.voidMethod("Hello");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testThrowExceptionWithProvidedException()
+    {
+        StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                trainee.voidMethod("Hello");
+                thenThrow(ObjectProviderUtils.constant(new IllegalArgumentException("Nope!")));
+            }
+        });
+        proxy.voidMethod("Hello");
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testThrowingExceptionObject()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one("Hello")).thenThrow(new RuntimeException("No way, Jose!"));
+            }
+        });
+        proxy.one("Hello");
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testThrowingProvidedException()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one("Hello")).thenThrow(
+                        ObjectProviderUtils.constant(new RuntimeException("No way, Jose!")));
+            }
+        });
+        proxy.one("Hello");
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testUsingWrongStub()
+    {
+        createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(final StubInterface parent)
+            {
+                when(parent.stub()).thenStub(new Trainer<StubInterface>()
+                {
+                    @Override
+                    protected void train(final StubInterface child)
+                    {
+                        when(parent.one("Hello")).thenReturn("World");
+                    }
+                });
+            }
+        });
+    }
+
+    @Test
+    public void testWithArgumentMatchers()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one(isInstance(String.class))).thenAnswer(ObjectProviderUtils.constant("World"));
+            }
+        });
+        assertEquals("World", proxy.one("Hello"));
+        assertEquals("World", proxy.one("Whatever"));
+    }
+
+    @Test
+    public void testWithArrayParameter()
+    {
+        StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.arrayParameter("One", "Two", "Three")).thenReturn("Four");
+            }
+        });
+
+        assertEquals("Four", proxy.arrayParameter("One", "Two", "Three"));
+    }
+
+    @Test
+    public void testWithBooleanArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.booleanArray()).thenReturn(false, true, false);
+            }
+        });
+        assertTrue(Arrays.equals(new boolean[] { false, true, false }, proxy.booleanArray()));
+    }
+
+    @Test
+    public void testWithByteArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.byteArray()).thenReturn((byte) 1, (byte) 2);
+            }
+        });
+        assertArrayEquals(new byte[] { 1, 2 }, proxy.byteArray());
+    }
+
+    @Test
+    public void testWithCharArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.charArray()).thenReturn('a', 'b', 'c');
+            }
+        });
+        assertArrayEquals(new char[] { 'a', 'b', 'c' }, proxy.charArray());
+    }
+
+    @Test
+    public void testWithDoubleArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.doubleArray()).thenReturn(1.0, 2.0);
+            }
+        });
+        assertArrayEquals(new double[] { 1.0, 2.0 }, proxy.doubleArray(), 0.0);
+    }
+
+    @Test
+    public void testWithFloatArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.floatArray()).thenReturn(1f, 2f);
+            }
+        });
+        assertArrayEquals(new float[] { 1f, 2f }, proxy.floatArray(), 0.0f);
+    }
+
+    @Test
+    public void testWithIntArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.intArray()).thenReturn(1, 2);
+            }
+        });
+        assertArrayEquals(new int[] { 1, 2 }, proxy.intArray());
+    }
+
+    @Test
+    public void testWithLongArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.longArray()).thenReturn(1, 2);
+            }
+        });
+        assertArrayEquals(new long[] { 1, 2 }, proxy.longArray());
+    }
+
+    @Test
+    public void testWithMismatchedArgument()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one(eq("Hello"))).thenReturn("World");
+            }
+        });
+        assertEquals("World", proxy.one("Hello"));
+        assertEquals(null, proxy.one("Whatever"));
+    }
+
+    @Test
+    public void testWithMultipleMethodsTrained()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one("Hello")).thenReturn("World");
+                when(trainee.two("Foo")).thenReturn("Bar");
+            }
+        });
+        assertEquals("World", proxy.one("Hello"));
+        assertEquals("Bar", proxy.two("Foo"));
+    }
+
+    @Test
+    public void testWithShortArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.shortArray()).thenReturn((short) 1, (short) 2);
+            }
+        });
+        assertArrayEquals(new short[] { 1, 2 }, proxy.shortArray());
+    }
+
+    @Test
+    public void testWithSingleMethodTrained()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one("Hello")).thenReturn("World");
+            }
+        });
+        assertEquals("World", proxy.one("Hello"));
+        assertEquals(null, proxy.two("Whatever"));
+        assertEquals(null, proxy.one("Mismatch!"));
+    }
+
+    @Test
+    public void testWithStringArray()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.stringArray()).thenReturn("One", "Two");
+            }
+        });
+        assertArrayEquals(new String[] { "One", "Two" }, proxy.stringArray());
+    }
+
+    /*
+     * This test replicates #thenStub() functionality in a more "ignorant" (ergo versatile) manner.
+     */
+    @Test
+    public void testInterruptResume()
+    {
+        final StubInterface proxy = createProxy(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.stub()).thenReturn(createProxy(new Trainer<StubInterface>()
+                {
+                    @Override
+                    protected void train(StubInterface trainee)
+                    {
+                        when(trainee.one("Hello")).thenReturn("World");
+                    }
+                }));
+            }
+        });
+        assertNotNull(proxy.stub());
+        assertEquals("World", proxy.stub().one("Hello"));
+    }
+}
diff --git a/test/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java b/test/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java
new file mode 100644
index 0000000..d6f2bf0
--- /dev/null
+++ b/test/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * Test {@link AnnotationFactory}.
+ */
+public class AnnotationBuilderTest
+{
+    @Test
+    public void testDefaultAnnotation()
+    {
+        final CustomAnnotation customAnnotation = AnnotationBuilder.buildDefault(CustomAnnotation.class);
+        assertEquals(CustomAnnotation.class, customAnnotation.annotationType());
+        assertEquals("", customAnnotation.annString());
+        assertEquals(0, customAnnotation.finiteValues().length);
+        assertNull(customAnnotation.someType());
+    }
+
+    @Test
+    public void testStubbedAnnotation()
+    {
+        final CustomAnnotation customAnnotation = AnnotationBuilder.of(CustomAnnotation.class)
+                .train(new AnnotationTrainer<CustomAnnotation>()
+                {
+                    @Override
+                    protected void train(CustomAnnotation trainee)
+                    {
+                        when(trainee.someType()).thenReturn(Object.class).when(trainee.finiteValues())
+                                .thenReturn(FiniteValues.ONE, FiniteValues.THREE).when(trainee.annString())
+                                .thenReturn("hey");
+                    }
+                }).build();
+
+        assertEquals(CustomAnnotation.class, customAnnotation.annotationType());
+        assertEquals("hey", customAnnotation.annString());
+        assertArrayEquals(new FiniteValues[] { FiniteValues.ONE, FiniteValues.THREE }, customAnnotation.finiteValues());
+        assertEquals(Object.class, customAnnotation.someType());
+    }
+
+    @Test
+    public void testNestedStubbedAnnotation()
+    {
+        final NestingAnnotation nestingAnnotation = AnnotationBuilder.of(NestingAnnotation.class)
+                .train(new AnnotationTrainer<NestingAnnotation>()
+                {
+                    @Override
+                    protected void train(NestingAnnotation trainee)
+                    {
+                        when(trainee.child()).thenStub(CustomAnnotation.class).when(trainee.somethingElse())
+                                .thenReturn("somethingElse");
+                    }
+                }).build();
+
+        assertEquals("", nestingAnnotation.child().annString());
+        assertEquals(0, nestingAnnotation.child().finiteValues().length);
+        assertEquals(null, nestingAnnotation.child().someType());
+        assertEquals("somethingElse", nestingAnnotation.somethingElse());
+    }
+
+    @Test
+    public void testMemberMap()
+    {
+        final Map<String, Object> members = new HashMap<String, Object>();
+        members.put("annString", "foo");
+        members.put("finiteValues", FiniteValues.values());
+        members.put("someType", Object.class);
+
+        final CustomAnnotation customAnnotation = AnnotationBuilder.of(CustomAnnotation.class).withMembers(members)
+                .build();
+
+        assertNotNull(customAnnotation);
+        assertEquals(CustomAnnotation.class, customAnnotation.annotationType());
+        assertEquals("foo", customAnnotation.annString());
+        assertEquals(3, customAnnotation.finiteValues().length);
+        assertEquals(Object.class, customAnnotation.someType());
+    }
+
+    @Test
+    public void testNestedStubbedAnnotationArray()
+    {
+        final NestingAnnotation nestingAnnotation = AnnotationBuilder.of(NestingAnnotation.class)
+                .train(new AnnotationTrainer<NestingAnnotation>()
+                {
+
+                    @Override
+                    protected void train(NestingAnnotation trainee)
+                    {
+                        when(trainee.children()).thenBuildArray().addElement(new AnnotationTrainer<CustomAnnotation>()
+                        {
+                            @Override
+                            protected void train(CustomAnnotation trainee)
+                            {
+                                when(trainee.finiteValues()).thenReturn(FiniteValues.ONE, FiniteValues.THREE);
+                            }
+                        }).addElement(new AnnotationTrainer<CustomAnnotation>()
+                        {
+                            @Override
+                            protected void train(CustomAnnotation trainee)
+                            {
+                                when(trainee.finiteValues()).thenReturn(FiniteValues.TWO);
+                            }
+                        }).build();
+                    }
+                }).build();
+
+        assertNull(nestingAnnotation.child());
+        assertEquals(2, nestingAnnotation.children().length);
+        assertArrayEquals(new FiniteValues[] { FiniteValues.ONE, FiniteValues.THREE },
+                nestingAnnotation.children()[0].finiteValues());
+        assertArrayEquals(new FiniteValues[] { FiniteValues.TWO }, nestingAnnotation.children()[1].finiteValues());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBadMemberMap()
+    {
+        AnnotationBuilder.of(CustomAnnotation.class).withMembers(
+                Collections.singletonMap("annString", Integer.valueOf(100)));
+    }
+
+    public @interface NestingAnnotation
+    {
+        CustomAnnotation child();
+
+        String somethingElse();
+
+        CustomAnnotation[] children() default {};
+    }
+
+    public @interface CustomAnnotation
+    {
+        String annString() default "";
+
+        FiniteValues[] finiteValues() default {};
+
+        Class<?> someType();
+    }
+
+    public enum FiniteValues
+    {
+        ONE, TWO, THREE;
+    }
+
+}
\ No newline at end of file
diff --git a/test/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java b/test/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java
new file mode 100644
index 0000000..2976eba
--- /dev/null
+++ b/test/src/test/java/org/apache/commons/proxy2/stub/RetentionWrapper.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 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();
+}
diff --git a/test/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java b/test/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java
new file mode 100644
index 0000000..d81ec2d
--- /dev/null
+++ b/test/src/test/java/org/apache/commons/proxy2/stub/StubBuilderTest.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.proxy2.ObjectProvider;
+import org.apache.commons.proxy2.provider.BeanProvider;
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+import org.junit.Test;
+
+public class StubBuilderTest extends AbstractStubTestCase
+{
+
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+
+    @Override
+    protected StubInterface createProxy(Trainer<StubInterface> trainer)
+    {
+        return new StubBuilder<StubInterface>(proxyFactory, StubInterface.class).train(trainer).build();
+    }
+
+    @Test
+    public void testWithConcreteTarget()
+    {
+        StubBuilder<StubInterface> builder = new StubBuilder<StubInterface>(proxyFactory, StubInterface.class, new SimpleStub());
+        builder.train(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one("Foo")).thenReturn("Bar");
+            }
+        });
+        StubInterface stub = builder.build();
+        assertEquals("Bar", stub.one("Foo"));
+    }
+
+    @Test
+    public void testWithNoTargetAndNoInterceptors()
+    {
+        StubBuilder<StubInterface> builder = new StubBuilder<StubInterface>(proxyFactory, StubInterface.class);
+        StubInterface stub = builder.build();
+        assertNull(stub.one("Whatever"));
+    }
+
+    @Test
+    public void testWithNoTargetWithInterceptor()
+    {
+        StubBuilder<StubInterface> builder = new StubBuilder<StubInterface>(proxyFactory, StubInterface.class);
+        builder.train(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one("Foo")).thenReturn("Bar");
+            }
+        });
+        StubInterface stub = builder.build();
+        assertEquals("Bar", stub.one("Foo"));
+    }
+
+    @Test
+    public void testWithObjectProviderTarget()
+    {
+        StubBuilder<StubInterface> builder = new StubBuilder<StubInterface>(proxyFactory, StubInterface.class, new BeanProvider<StubInterface>(SimpleStub.class));
+        builder.train(new Trainer<StubInterface>()
+        {
+            @Override
+            protected void train(StubInterface trainee)
+            {
+                when(trainee.one("Foo")).thenReturn("Bar");
+            }
+        });
+        StubInterface stub = builder.build();
+        assertEquals("Bar", stub.one("Foo"));
+    }
+
+    @Test
+    public void testAdditionalInterfaces() {
+        StubBuilder<StubInterface> builder = new StubBuilder<StubInterface>(proxyFactory, StubInterface.class,
+                ObjectProviderUtils.constant(new SimpleStub()));
+        builder.train(new Trainer<Iterable<String>>()
+        {
+
+            @Override
+            protected void train(Iterable<String> trainee)
+            {
+                when(trainee.iterator()).thenAnswer(new ObjectProvider<Iterator<String>>()
+                {
+                    @Override
+                    public Iterator<String> getObject()
+                    {
+                        return Arrays.asList("foo", "bar", "baz").iterator();
+                    }
+                });
+            }
+        });
+        builder.addProxyTypes(Cloneable.class, Marker.class);
+        StubInterface stub = builder.build();
+        assertTrue(stub instanceof Iterable<?>);
+        assertTrue(stub instanceof Cloneable);
+        assertTrue(stub instanceof Marker);
+    }
+
+//----------------------------------------------------------------------------------------------------------------------
+// Inner Classes
+//----------------------------------------------------------------------------------------------------------------------
+
+    private static class SimpleStub implements StubInterface
+    {
+        @Override
+        public String one(String value)
+        {
+            return value;
+        }
+
+        @Override
+        public String three(String arg1, String arg2)
+        {
+            return arg1 + arg2;
+        }
+
+        @Override
+        public String two(String value)
+        {
+            return StringUtils.repeat(value, 2);
+        }
+
+        @Override
+        public byte[] byteArray()
+        {
+            return new byte[]{1, 2, 3};
+        }
+
+        @Override
+        public char[] charArray()
+        {
+            return new char[]{'1', '2', '3'};
+        }
+
+        @Override
+        public short[] shortArray()
+        {
+            return new short[]{1, 2, 3};
+        }
+
+        @Override
+        public int[] intArray()
+        {
+            return new int[]{1, 2, 3};
+        }
+
+        @Override
+        public long[] longArray()
+        {
+            return new long[]{1, 2, 3};
+        }
+
+        @Override
+        public float[] floatArray()
+        {
+            return new float[]{1.0f, 2.0f, 3.0f};
+        }
+
+        @Override
+        public double[] doubleArray()
+        {
+            return new double[]{1.0, 2.0, 3.0};
+        }
+
+        @Override
+        public boolean[] booleanArray()
+        {
+            return new boolean[]{true, false, true};
+        }
+
+        @Override
+        public String[] stringArray()
+        {
+            return new String[]{"One", "Two", "Three"};
+        }
+
+        @Override
+        public String arrayParameter(String... strings)
+        {
+            return StringUtils.join(strings, ", ");
+        }
+
+        @Override
+        public void voidMethod(String arg)
+        {
+
+        }
+
+        @Override
+        public StubInterface stub()
+        {
+            return null;
+        }
+
+        @Override
+        public StubInterface[] stubs()
+        {
+            return new StubInterface[0];
+        }
+    }
+
+    public interface Marker
+    {
+    }
+}
diff --git a/test/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java b/test/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java
new file mode 100644
index 0000000..618038b
--- /dev/null
+++ b/test/src/test/java/org/apache/commons/proxy2/stub/StubInterceptorBuilderTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.invoker.NullInvoker;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import static org.junit.Assert.*;
+
+public class StubInterceptorBuilderTest extends AbstractStubTestCase
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Fields
+//----------------------------------------------------------------------------------------------------------------------
+
+    private StubInterceptorBuilder builder;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    @Before
+    public void initialize()
+    {
+        builder = new StubInterceptorBuilder(proxyFactory);
+    }
+
+    @Override
+    protected StubInterface createProxy(Trainer<StubInterface> trainer)
+    {
+        Interceptor interceptor = builder.train(trainer).build();
+        return proxyFactory.createInterceptorProxy(
+                proxyFactory.createInvokerProxy(NullInvoker.INSTANCE, StubInterface.class),
+                interceptor,
+                StubInterface.class);
+    }
+
+    @Test
+    public void testWithNestedAnnotations()
+    {
+        Interceptor interceptor = builder.train(new Trainer<RetentionWrapper>()
+        {
+            @Override
+            protected void train(RetentionWrapper trainee)
+            {
+
+                when(trainee.value()).thenStub(new Trainer<Retention>()
+                {
+                    @Override
+                    protected void train(Retention trainee)
+                    {
+                        when(trainee.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.train(new Trainer<Retention>()
+        {
+            @Override
+            protected void train(Retention trainee)
+            {
+                when(trainee.value()).thenReturn(RetentionPolicy.RUNTIME);
+            }
+        }).build();
+        Retention wrapper = proxyFactory.createInterceptorProxy(proxyFactory.createInvokerProxy(NullInvoker.INSTANCE), interceptor, Retention.class);
+        assertEquals(RetentionPolicy.RUNTIME, wrapper.value());
+    }
+
+}
diff --git a/test/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java b/test/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java
new file mode 100644
index 0000000..1812f6b
--- /dev/null
+++ b/test/src/test/java/org/apache/commons/proxy2/stub/StubInterface.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;
+
+public interface StubInterface
+{
+//----------------------------------------------------------------------------------------------------------------------
+// Other Methods
+//----------------------------------------------------------------------------------------------------------------------
+
+    String one(String value);
+    String three(String arg1, String arg2);
+    String two(String value);
+
+    byte[] byteArray();
+    char[] charArray();
+    short[] shortArray();
+    int[] intArray();
+    long[] longArray();
+    float[] floatArray();
+    double[] doubleArray();
+    boolean[] booleanArray();
+    String[] stringArray();
+
+    String arrayParameter(String... strings);
+
+    void voidMethod(String arg);
+
+    StubInterface stub();
+    StubInterface[] stubs();
+}