work in progress.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/proxy/branches/version-2.0-work@964636 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/cglib/pom.xml b/cglib/pom.xml
new file mode 100644
index 0000000..b4d3193
--- /dev/null
+++ b/cglib/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<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-proxy</artifactId>

+        <groupId>org.apache.commons</groupId>

+        <version>2.0-SNAPSHOT</version>

+    </parent>

+    <modelVersion>4.0.0</modelVersion>

+

+    <artifactId>commons-proxy-cglib</artifactId>

+

+    <dependencies>

+        <dependency>

+            <groupId>${project.groupId}</groupId>

+            <artifactId>commons-proxy-core</artifactId>

+            <version>${project.version}</version>

+        </dependency>

+        <dependency>

+            <groupId>cglib</groupId>

+            <artifactId>cglib-nodep</artifactId>

+            <version>2.1_3</version>

+            <optional>true</optional>

+        </dependency>

+        <dependency>

+            <groupId>${project.groupId}</groupId>

+            <artifactId>commons-proxy-core</artifactId>

+            <version>${project.version}</version>

+            <type>test-jar</type>

+            <scope>test</scope>

+        </dependency>

+        <dependency>

+            <groupId>commons-lang</groupId>

+            <artifactId>commons-lang</artifactId>

+            <version>2.3</version>

+            <scope>test</scope>

+        </dependency>

+        <dependency>

+            <groupId>junit</groupId>

+            <artifactId>junit</artifactId>

+            <version>3.8.1</version>

+            <scope>test</scope>

+        </dependency>

+    </dependencies>

+

+</project>

diff --git a/cglib/src/main/java/org/apache/commons/proxy/cglib/CglibProxyFactory.java b/cglib/src/main/java/org/apache/commons/proxy/cglib/CglibProxyFactory.java
new file mode 100644
index 0000000..2436f60
--- /dev/null
+++ b/cglib/src/main/java/org/apache/commons/proxy/cglib/CglibProxyFactory.java
@@ -0,0 +1,187 @@
+package org.apache.commons.proxy.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.MethodInterceptor;

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

+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.impl.AbstractSubclassingProxyFactory;

+

+import java.io.Serializable;

+import java.lang.reflect.Method;

+

+public class CglibProxyFactory extends AbstractSubclassingProxyFactory

+{

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

+// Fields

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

+

+    private static CallbackFilter callbackFilter = new CglibProxyFactoryCallbackFilter();

+

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

+// ProxyFactory Implementation

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

+

+    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 CglibProxyFactoryCallbackFilter implements CallbackFilter

+    {

+        public int accept(Method method)

+        {

+            if (isEqualsMethod(method))

+            {

+                return 1;

+            }

+            else if (isHashCode(method))

+            {

+                return 2;

+            }

+            else

+            {

+                return 0;

+            }

+        }

+    }

+

+    private static class EqualsHandler implements MethodInterceptor, Serializable

+    {

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

+        {

+            return o == objects[0];

+        }

+    }

+

+    private static class HashCodeHandler implements MethodInterceptor, Serializable

+    {

+        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

+    {

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

+        }

+    }

+}

diff --git a/cglib/src/test/java/org/apache/commons/proxy/cglib/TestCglibProxyFactory.java b/cglib/src/test/java/org/apache/commons/proxy/cglib/TestCglibProxyFactory.java
new file mode 100644
index 0000000..4bf7c24
--- /dev/null
+++ b/cglib/src/test/java/org/apache/commons/proxy/cglib/TestCglibProxyFactory.java
@@ -0,0 +1,15 @@
+package org.apache.commons.proxy.cglib;

+

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

+

+public class TestCglibProxyFactory extends AbstractSubclassingProxyFactoryTestCase

+{

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

+// Constructors

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

+

+    public TestCglibProxyFactory()

+    {

+        super(new CglibProxyFactory());

+    }

+}

diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..c72654a
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<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-proxy</artifactId>

+        <groupId>org.apache.commons</groupId>

+        <version>2.0-SNAPSHOT</version>

+    </parent>

+    <modelVersion>4.0.0</modelVersion>

+

+    <artifactId>commons-proxy-core</artifactId>

+

+    <dependencies>

+        <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>xmlrpc</groupId>

+            <artifactId>xmlrpc</artifactId>

+            <version>2.0</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.0</version>

+            <optional>true</optional>

+        </dependency>

+        <dependency>

+            <groupId>commons-collections</groupId>

+            <artifactId>commons-collections</artifactId>

+            <version>3.1</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>

+        <dependency>

+            <groupId>jmock</groupId>

+            <artifactId>jmock</artifactId>

+            <version>1.0.1</version>

+            <scope>test</scope>

+        </dependency>

+        <dependency>

+            <groupId>junit</groupId>

+            <artifactId>junit</artifactId>

+            <version>3.8.1</version>

+            <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/proxy/Interceptor.java b/core/src/main/java/org/apache/commons/proxy/Interceptor.java
new file mode 100644
index 0000000..5d04fd4
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/Interceptor.java
@@ -0,0 +1,35 @@
+/*

+ * 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;

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/Invocation.java b/core/src/main/java/org/apache/commons/proxy/Invocation.java
new file mode 100644
index 0000000..be47b32
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/Invocation.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.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;

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/Invoker.java b/core/src/main/java/org/apache/commons/proxy/Invoker.java
new file mode 100644
index 0000000..fb3bbfa
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/Invoker.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.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;

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/ObjectProvider.java b/core/src/main/java/org/apache/commons/proxy/ObjectProvider.java
new file mode 100644
index 0000000..2e3e240
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/ObjectProvider.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.proxy;

+

+/**

+ * 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.proxy.exception.ObjectProviderException} if any problems arise while

+     * constructing/finding the object.

+     *

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

+     */

+    public T getObject();

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/ProxyFactory.java b/core/src/main/java/org/apache/commons/proxy/ProxyFactory.java
new file mode 100644
index 0000000..4faba4b
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/ProxyFactory.java
@@ -0,0 +1,161 @@
+package org.apache.commons.proxy;

+

+public interface ProxyFactory

+{

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

+// Other Methods

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

+

+    /**

+     * 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 proxyClass     the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings( "unchecked" )

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

+                                       Class<T> proxyClass );

+

+    /**

+     * 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 proxyClass the class/interface that the proxy should implement

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

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

+     */

+    @SuppressWarnings( "unchecked" )

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

+                                         Class<T> proxyClass );

+

+    /**

+     * 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 proxyClass the class/interface that the proxy should implement.

+     * @return a proxy 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<T> proxyClass );

+

+    /**

+     * 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 proxyClass the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings( "unchecked" )

+    public <T> T createInvokerProxy( Invoker invoker, Class<T> proxyClass );

+

+    /**

+     * 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 proxyClass the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings( "unchecked" )

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

+                                     Class<T> proxyClass );

+

+    /**

+     * 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 proxyClass     the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createDelegatorProxy( ObjectProvider<T> delegateProvider, Class<T> proxyClass );

+    /**

+     * 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 );

+

+    /**

+     * 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 );

+

+    /**

+     * 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 );

+

+    /**

+     * 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 );

+

+    /**

+     * 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 );

+

+    /**

+     * 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 );

+

+    /**

+     * 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 );

+}

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

+ * 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.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 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> wrapperClassMap = new HashMap<Class, Class>();

+    private static Map<Class, Object> nullValueMap = new HashMap<Class, Object>();

+

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

+// 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

+    {

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

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

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

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

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

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

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

+        nullValueMap.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 wrapperClassMap.get(primitiveType);

+    }

+

+    /**

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

+     *

+     * @param type the type

+     * @return the null value

+     */

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

+    {

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

+    }

+

+    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]);

+    }

+

+    public static boolean isHashCode(Method method)

+    {

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

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

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

+    }

+

+}

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

+ * 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/core/src/main/java/org/apache/commons/proxy/exception/ObjectProviderException.java b/core/src/main/java/org/apache/commons/proxy/exception/ObjectProviderException.java
new file mode 100644
index 0000000..a7b6979
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/exception/ObjectProviderException.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.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/core/src/main/java/org/apache/commons/proxy/exception/ProxyFactoryException.java b/core/src/main/java/org/apache/commons/proxy/exception/ProxyFactoryException.java
new file mode 100644
index 0000000..136d6ae
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/exception/ProxyFactoryException.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.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);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/impl/AbstractProxyClassGenerator.java b/core/src/main/java/org/apache/commons/proxy/impl/AbstractProxyClassGenerator.java
new file mode 100644
index 0000000..1269090
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/impl/AbstractProxyClassGenerator.java
@@ -0,0 +1,78 @@
+/*

+ * 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.impl;

+

+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()]);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/impl/AbstractProxyFactory.java b/core/src/main/java/org/apache/commons/proxy/impl/AbstractProxyFactory.java
new file mode 100644
index 0000000..01bd8dc
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/impl/AbstractProxyFactory.java
@@ -0,0 +1,310 @@
+package org.apache.commons.proxy.impl;

+

+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.ProxyUtils;

+

+import java.io.Serializable;

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.lang.reflect.Proxy;

+

+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 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>.  The proxy will be

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

+     *

+     * @param delegateProvider the delegate provider

+     * @param proxyClass     the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings("unchecked")

+    public <T> T createDelegatorProxy( ObjectProvider<T> delegateProvider, Class<T> proxyClass )

+    {

+        return (T)createDelegatorProxy(delegateProvider, new Class[] {proxyClass});

+    }

+

+    /**

+     * 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 proxyClass     the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings( "unchecked" )

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

+                                       Class<T> proxyClass )

+    {

+        return ( T ) createDelegatorProxy(classLoader, delegateProvider, new Class[] {proxyClass});

+    }

+

+    /**

+     * 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.  The proxy will be generated using the current thread's "context class loader."

+     *

+     * @param target       the target object

+     * @param interceptor  the method interceptor

+     * @param proxyClass the class/interface that the proxy should implement

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

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

+     */

+    @SuppressWarnings( "unchecked" )

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

+                                         Class<T> proxyClass )

+    {

+        return ( T ) createInterceptorProxy(target, interceptor, new Class[] {proxyClass});

+    }

+

+    /**

+     * 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 proxyClass the class/interface that the proxy should implement.

+     * @return a proxy 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<T> proxyClass )

+    {

+        return ( T ) createInterceptorProxy(classLoader, target, interceptor, new Class[] {proxyClass});

+    }

+

+    /**

+     * 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.  The proxy will be

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

+     *

+     * @param invoker      the invoker

+     * @param proxyClass the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings( "unchecked" )

+    public <T> T createInvokerProxy( Invoker invoker, Class<T> proxyClass )

+    {

+        return ( T ) createInvokerProxy(invoker, new Class[] {proxyClass});

+    }

+

+    /**

+     * 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 proxyClass the class/interface that the proxy should implement

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

+     */

+    @SuppressWarnings( "unchecked" )

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

+                                     Class<T> proxyClass )

+    {

+        return ( T ) createInvokerProxy(classLoader, invoker, new Class[] {proxyClass});

+    }

+

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

+// 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 System.identityHashCode(proxy);

+            }

+            else if( 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 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/core/src/main/java/org/apache/commons/proxy/impl/AbstractSubclassingProxyFactory.java b/core/src/main/java/org/apache/commons/proxy/impl/AbstractSubclassingProxyFactory.java
new file mode 100644
index 0000000..cb4f98f
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/impl/AbstractSubclassingProxyFactory.java
@@ -0,0 +1,144 @@
+package org.apache.commons.proxy.impl;

+

+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;

+

+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 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()]);

+    }

+

+    /**

+     * 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()]);

+    }

+

+    /**

+     * 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());

+        }

+    }

+}

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

+ * 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.impl;

+

+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/core/src/main/java/org/apache/commons/proxy/impl/ProxyClassCache.java b/core/src/main/java/org/apache/commons/proxy/impl/ProxyClassCache.java
new file mode 100644
index 0000000..1aa1434
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/impl/ProxyClassCache.java
@@ -0,0 +1,115 @@
+/*

+ * 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.impl;

+

+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;

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/impl/ProxyClassGenerator.java b/core/src/main/java/org/apache/commons/proxy/impl/ProxyClassGenerator.java
new file mode 100644
index 0000000..01aa883
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/impl/ProxyClassGenerator.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.proxy.impl;

+

+/**

+ * 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 );

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/invoker/DuckTypingInvoker.java b/core/src/main/java/org/apache/commons/proxy/invoker/DuckTypingInvoker.java
new file mode 100644
index 0000000..a3f1a37
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/invoker/DuckTypingInvoker.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.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 + ".");

+        }

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/invoker/InvocationHandlerAdapter.java b/core/src/main/java/org/apache/commons/proxy/invoker/InvocationHandlerAdapter.java
new file mode 100644
index 0000000..8dc3acb
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/invoker/InvocationHandlerAdapter.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.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);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/invoker/NullInvoker.java b/core/src/main/java/org/apache/commons/proxy/invoker/NullInvoker.java
new file mode 100644
index 0000000..522d4f3
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/invoker/NullInvoker.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.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.nullValue(returnType);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/invoker/XmlRpcInvoker.java b/core/src/main/java/org/apache/commons/proxy/invoker/XmlRpcInvoker.java
new file mode 100644
index 0000000..065e494
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/invoker/XmlRpcInvoker.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.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/core/src/main/java/org/apache/commons/proxy/invoker/recorder/InvocationRecorder.java b/core/src/main/java/org/apache/commons/proxy/invoker/recorder/InvocationRecorder.java
new file mode 100644
index 0000000..3a30702
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/invoker/recorder/InvocationRecorder.java
@@ -0,0 +1,126 @@
+/*

+ * 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.recorder;

+

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

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

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

+

+import java.lang.reflect.Method;

+import java.lang.reflect.ParameterizedType;

+import java.lang.reflect.Type;

+import java.lang.reflect.TypeVariable;

+import java.util.LinkedList;

+import java.util.List;

+

+/**

+ * @auothor James Carman

+ */

+public class InvocationRecorder

+{

+    private final ProxyFactory proxyFactory;

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

+

+    public InvocationRecorder( ProxyFactory proxyFactory )

+    {

+        this.proxyFactory = proxyFactory;

+    }

+

+    public List<RecordedInvocation> getRecordedInvocations()

+    {

+        return recordedInvocations;

+    }

+

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

+    {

+        return proxy(type, type);

+    }

+

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

+    {

+        if( proxyFactory.canProxy(type) )

+        {

+            return proxyFactory.createInvokerProxy(new InvocationRecorderInvoker(genericType), type);

+        }

+        return ProxyUtils.nullValue(type);

+    }

+

+    private class InvocationRecorderInvoker implements Invoker

+    {

+        private final Type targetType;

+

+        private InvocationRecorderInvoker( Type targetType )

+        {

+            this.targetType = targetType;

+        }

+

+        @SuppressWarnings("unchecked")

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

+        {

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

+            final Class returnType = getReturnType(targetType, method);

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

+        }

+    }

+

+    public void reset()

+    {

+        recordedInvocations.clear();

+    }

+

+    public static Class getReturnType( Type enclosingType, Method method ) throws Exception

+    {

+        Type returnType = method.getGenericReturnType();

+        if( returnType instanceof Class )

+        {

+            return ( Class ) returnType;

+        }

+        else if( returnType instanceof TypeVariable )

+        {

+            return resolveVariable(enclosingType, ( TypeVariable ) returnType);

+        }

+        else if( returnType instanceof ParameterizedType )

+        {

+            return ( Class ) ( ( ParameterizedType ) returnType ).getRawType();

+        }

+        return null;

+    }

+

+    public static Class resolveVariable( Type enclosingType, TypeVariable typeVar ) throws Exception

+    {

+        if( enclosingType instanceof ParameterizedType )

+        {

+            ParameterizedType pt = ( ParameterizedType ) enclosingType;

+            final Class rawType = ( Class ) pt.getRawType();

+            TypeVariable[] typeParameters = rawType.getTypeParameters();

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

+            {

+                TypeVariable typeParameter = typeParameters[i];

+                if( typeParameter == typeVar )

+                {

+                    return ( Class ) pt.getActualTypeArguments()[i];

+                }

+            }

+        }

+        else if( enclosingType instanceof Class )

+        {

+            return resolveVariable(( ( Class ) enclosingType ).getGenericSuperclass(), typeVar);

+        }

+        return null;

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/invoker/recorder/RecordedInvocation.java b/core/src/main/java/org/apache/commons/proxy/invoker/recorder/RecordedInvocation.java
new file mode 100644
index 0000000..9eafbba
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/invoker/recorder/RecordedInvocation.java
@@ -0,0 +1,116 @@
+/*

+ * 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.recorder;

+

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

+

+import java.lang.reflect.Method;

+

+/**

+ * @auothor James Carman

+ */

+public class RecordedInvocation

+{

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

+// Fields

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

+

+    private final Method invokedMethod;

+    private final Object[] arguments;

+

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

+// Constructors

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

+

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

+    {

+        this.invokedMethod = invokedMethod;

+        this.arguments = arguments;

+    }

+

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

+// Canonical Methods

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

+

+    public Method getInvokedMethod()

+    {

+        return invokedMethod;

+    }

+

+    public Object[] getArguments()

+    {

+        return arguments;

+    }

+

+    public String toString()

+    {

+        StringBuffer buffer = new StringBuffer();

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

+    }

+

+    protected void convert( StringBuffer 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("}");

+        }

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/provider/BeanProvider.java b/core/src/main/java/org/apache/commons/proxy/provider/BeanProvider.java
new file mode 100644
index 0000000..63be4f4
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/provider/BeanProvider.java
@@ -0,0 +1,90 @@
+/*

+ * 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<T> implements ObjectProvider<T>, Serializable

+{

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

+// Fields

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

+

+    private Class<T> beanClass;

+

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

+// Constructors

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

+

+    public BeanProvider()

+    {

+    }

+

+    /**

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

+     *

+     * @param beanClass the bean class

+     */

+    public BeanProvider( Class<T> beanClass )

+    {

+        this.beanClass = beanClass;

+    }

+

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

+// ObjectProvider Implementation

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

+

+    public T 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<T> beanClass )

+    {

+        this.beanClass = beanClass;

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/provider/CloningProvider.java b/core/src/main/java/org/apache/commons/proxy/provider/CloningProvider.java
new file mode 100644
index 0000000..1a78bbc
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/provider/CloningProvider.java
@@ -0,0 +1,101 @@
+/*

+ * 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<T extends Cloneable> implements ObjectProvider<T>, Serializable

+{

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

+// Fields

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

+

+    private final T 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( T cloneable )

+    {

+        this.cloneable = cloneable;

+    }

+

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

+// ObjectProvider Implementation

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

+

+    @SuppressWarnings("unchecked")

+    public T getObject()

+    {

+        try

+        {

+            return (T)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;

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/provider/ConstantProvider.java b/core/src/main/java/org/apache/commons/proxy/provider/ConstantProvider.java
new file mode 100644
index 0000000..b6322f5
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/provider/ConstantProvider.java
@@ -0,0 +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.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<T> implements ObjectProvider<T>, Serializable

+{

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

+// Fields

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

+

+    private final T constant;

+

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

+// Constructors

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

+

+    public ConstantProvider( T constant )

+    {

+        this.constant = constant;

+    }

+

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

+// ObjectProvider Implementation

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

+

+    public T getObject()

+    {

+        return constant;

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/provider/NullProvider.java b/core/src/main/java/org/apache/commons/proxy/provider/NullProvider.java
new file mode 100644
index 0000000..444a322
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/provider/NullProvider.java
@@ -0,0 +1,36 @@
+/*

+ * 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<T> extends ConstantProvider<T>

+{

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

+// Constructors

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

+

+    public NullProvider()

+    {

+        super(null);

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/provider/ProviderDecorator.java b/core/src/main/java/org/apache/commons/proxy/provider/ProviderDecorator.java
new file mode 100644
index 0000000..f577581
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/provider/ProviderDecorator.java
@@ -0,0 +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.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<T> implements ObjectProvider<T>

+{

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

+// Fields

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

+

+    protected ObjectProvider<T> inner;

+

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

+// Constructors

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

+

+    public ProviderDecorator( ObjectProvider<T> inner )

+    {

+        this.inner = inner;

+    }

+

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

+// ObjectProvider Implementation

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

+

+    public T getObject()

+    {

+        return inner.getObject();

+    }

+}

diff --git a/core/src/main/java/org/apache/commons/proxy/provider/SingletonProvider.java b/core/src/main/java/org/apache/commons/proxy/provider/SingletonProvider.java
new file mode 100644
index 0000000..d9cd37c
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy/provider/SingletonProvider.java
@@ -0,0 +1,62 @@
+/*

+ * 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;

+

+/**

+ * 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

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

+

+    public SingletonProvider( ObjectProvider<T> inner )

+    {

+        super(inner);

+    }

+

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

+// ObjectProvider Implementation

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

+

+    public T getObject()

+    {

+        synchronized( this )

+        {

+            if( instance == null )

+            {

+                instance = super.getObject();

+                inner = null; // Garbage collection

+            }

+        }

+        return instance;

+    }

+}

diff --git a/core/src/test/java/org/apache/commons/proxy/AbstractProxyFactoryTestCase.java b/core/src/test/java/org/apache/commons/proxy/AbstractProxyFactoryTestCase.java
new file mode 100644
index 0000000..ec5ea88
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/AbstractProxyFactoryTestCase.java
@@ -0,0 +1,392 @@
+/*

+ * 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.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.Date;

+import java.util.LinkedList;

+import java.util.List;

+import java.util.SortedSet;

+import java.util.TreeSet;

+

+/**

+ * @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 = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        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>(date),

+                COMPARABLE_ONLY);

+        final Comparable proxy2 = (Comparable) factory.createDelegatorProxy(new ConstantProvider<Date>(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.class));

+        assertFalse(factory.canProxy(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 = 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());

+    }

+

+    public void testDelegatingProxyInterfaceOrder()

+    {

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

+        final List<Class> expected = new LinkedList<Class>(Arrays.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);

+    }

+

+    public void testDelegatingProxySerializable() throws Exception

+    {

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

+        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)

+        {

+        }

+    }

+

+    @SuppressWarnings("unchecked")

+    public void testInterfaceHierarchies()

+    {

+        final SortedSet set = factory.createDelegatorProxy(new ConstantProvider<SortedSet>(new TreeSet()), 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", 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, 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, Echo.class, DuplicateEcho.class);

+        proxy.echoBack("hello");

+        assertEquals(Echo.class.getMethod("echoBack", 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"), 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", 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);

+    }

+

+    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 = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);

+        try

+        {

+            proxy.ioException();

+            fail();

+        }

+        catch (IOException e)

+        {

+        }

+    }

+

+    public void testProxyWithUncheckedException() throws Exception

+    {

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

+        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/core/src/test/java/org/apache/commons/proxy/AbstractSubclassingProxyFactoryTestCase.java b/core/src/test/java/org/apache/commons/proxy/AbstractSubclassingProxyFactoryTestCase.java
new file mode 100644
index 0000000..3b413eb
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/AbstractSubclassingProxyFactoryTestCase.java
@@ -0,0 +1,222 @@
+/*

+ * 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.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/core/src/test/java/org/apache/commons/proxy/util/AbstractEcho.java b/core/src/test/java/org/apache/commons/proxy/util/AbstractEcho.java
new file mode 100644
index 0000000..1486669
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/util/AbstractEcho.java
@@ -0,0 +1,36 @@
+/*

+ * 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;

+    }

+}

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

+

+import junit.framework.TestCase;

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

+

+import java.io.Serializable;

+

+/**

+ * @auothor 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/core/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java b/core/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
new file mode 100644
index 0000000..b96cbb6
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/util/DuplicateEcho.java
@@ -0,0 +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 );

+}

diff --git a/core/src/test/java/org/apache/commons/proxy/util/Echo.java b/core/src/test/java/org/apache/commons/proxy/util/Echo.java
new file mode 100644
index 0000000..60ea278
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/util/Echo.java
@@ -0,0 +1,47 @@
+/*

+ * 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;

+}

diff --git a/core/src/test/java/org/apache/commons/proxy/util/EchoImpl.java b/core/src/test/java/org/apache/commons/proxy/util/EchoImpl.java
new file mode 100644
index 0000000..af53333
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/util/EchoImpl.java
@@ -0,0 +1,79 @@
+/*

+ * 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");

+    }

+}

diff --git a/core/src/test/java/org/apache/commons/proxy/util/QuoteService.java b/core/src/test/java/org/apache/commons/proxy/util/QuoteService.java
new file mode 100644
index 0000000..40293cb
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/util/QuoteService.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.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;

+}

diff --git a/core/src/test/java/org/apache/commons/proxy/util/SuffixInterceptor.java b/core/src/test/java/org/apache/commons/proxy/util/SuffixInterceptor.java
new file mode 100644
index 0000000..84937c7
--- /dev/null
+++ b/core/src/test/java/org/apache/commons/proxy/util/SuffixInterceptor.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.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;

+    }

+}

diff --git a/javassist/pom.xml b/javassist/pom.xml
new file mode 100644
index 0000000..743c2d4
--- /dev/null
+++ b/javassist/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<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-proxy</artifactId>

+        <groupId>org.apache.commons</groupId>

+        <version>2.0-SNAPSHOT</version>

+    </parent>

+    <modelVersion>4.0.0</modelVersion>

+

+    <artifactId>commons-proxy-javassist</artifactId>

+

+    <dependencies>

+        <dependency>

+            <groupId>${project.groupId}</groupId>

+            <artifactId>commons-proxy-core</artifactId>

+            <version>${project.version}</version>

+        </dependency>

+        <dependency>

+            <groupId>jboss</groupId>

+            <artifactId>javassist</artifactId>

+            <version>3.0</version>

+        </dependency>

+        <dependency>

+            <groupId>${project.groupId}</groupId>

+            <artifactId>commons-proxy-core</artifactId>

+            <version>${project.version}</version>

+            <type>test-jar</type>

+            <scope>test</scope>

+        </dependency>

+        <dependency>

+            <groupId>commons-lang</groupId>

+            <artifactId>commons-lang</artifactId>

+            <version>2.3</version>

+            <scope>test</scope>

+        </dependency>

+        <dependency>

+            <groupId>junit</groupId>

+            <artifactId>junit</artifactId>

+            <version>3.8.1</version>

+            <scope>test</scope>

+        </dependency>

+    </dependencies>

+</project>

diff --git a/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistInvocation.java b/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistInvocation.java
new file mode 100644
index 0000000..1f965d5
--- /dev/null
+++ b/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistInvocation.java
@@ -0,0 +1,222 @@
+/*

+ * 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.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;

+    }

+}

diff --git a/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistProxyFactory.java b/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistProxyFactory.java
new file mode 100644
index 0000000..85fe624
--- /dev/null
+++ b/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistProxyFactory.java
@@ -0,0 +1,255 @@
+package org.apache.commons.proxy.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.impl.AbstractProxyClassGenerator;

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

+import org.apache.commons.proxy.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 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(ObjectProvider.class)

+                    .newInstance(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(Object.class, Interceptor.class)

+                    .newInstance(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(Invoker.class)

+                    .newInstance(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);

+            }

+        }

+    }

+}

diff --git a/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistUtils.java b/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistUtils.java
new file mode 100644
index 0000000..5a41b12
--- /dev/null
+++ b/javassist/src/main/java/org/apache/commons/proxy/javassist/JavassistUtils.java
@@ -0,0 +1,154 @@
+/*

+ * 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.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;

+    }

+}

diff --git a/javassist/src/test/java/org/apache/commons/proxy/javassist/TestJavassistProxyFactory.java b/javassist/src/test/java/org/apache/commons/proxy/javassist/TestJavassistProxyFactory.java
new file mode 100644
index 0000000..1421753
--- /dev/null
+++ b/javassist/src/test/java/org/apache/commons/proxy/javassist/TestJavassistProxyFactory.java
@@ -0,0 +1,16 @@
+package org.apache.commons.proxy.javassist;

+

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

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

+

+public class TestJavassistProxyFactory extends AbstractSubclassingProxyFactoryTestCase

+{

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

+// Constructors

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

+

+    public TestJavassistProxyFactory()

+    {

+        super(new JavassistProxyFactory());

+    }

+}

diff --git a/jdk/pom.xml b/jdk/pom.xml
new file mode 100644
index 0000000..9893f00
--- /dev/null
+++ b/jdk/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<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-proxy</artifactId>

+        <groupId>org.apache.commons</groupId>

+        <version>2.0-SNAPSHOT</version>

+    </parent>

+    <modelVersion>4.0.0</modelVersion>

+

+    <artifactId>commons-proxy-jdk</artifactId>

+

+    <dependencies>

+        <dependency>

+            <groupId>${project.groupId}</groupId>

+            <artifactId>commons-proxy-core</artifactId>

+            <version>${project.version}</version>

+        </dependency>

+        <dependency>

+            <groupId>${project.groupId}</groupId>

+            <artifactId>commons-proxy-core</artifactId>

+            <version>${project.version}</version>

+            <type>test-jar</type>

+            <scope>test</scope>

+        </dependency>

+        <dependency>

+            <groupId>commons-lang</groupId>

+            <artifactId>commons-lang</artifactId>

+            <version>2.3</version>

+            <scope>test</scope>

+        </dependency>

+        <dependency>

+            <groupId>junit</groupId>

+            <artifactId>junit</artifactId>

+            <version>3.8.1</version>

+            <scope>test</scope>

+        </dependency>

+    </dependencies>

+</project>

diff --git a/jdk/src/main/java/org/apache/commons/proxy/jdk/JdkProxyFactory.java b/jdk/src/main/java/org/apache/commons/proxy/jdk/JdkProxyFactory.java
new file mode 100644
index 0000000..b56ff25
--- /dev/null
+++ b/jdk/src/main/java/org/apache/commons/proxy/jdk/JdkProxyFactory.java
@@ -0,0 +1,190 @@
+package org.apache.commons.proxy.jdk;

+

+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.ProxyUtils;

+import org.apache.commons.proxy.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;

+

+public class JdkProxyFactory extends AbstractProxyFactory

+{    

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

+// ProxyFactory Implementation

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

+

+    

+    /**

+     * 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.

+     *

+     * @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.

+     *

+     * @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 abstract static class AbstractInvocationHandler implements InvocationHandler, Serializable

+    {

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

+        {

+            if( isHashCode(method) )

+            {

+                return System.identityHashCode(proxy);

+            }

+            else if( 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

+    {

+        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 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);

+        }

+    }

+

+    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/jdk/src/test/java/org/apache/commons/proxy/jdk/TestJdkProxyFactory.java b/jdk/src/test/java/org/apache/commons/proxy/jdk/TestJdkProxyFactory.java
new file mode 100644
index 0000000..bcbe47c
--- /dev/null
+++ b/jdk/src/test/java/org/apache/commons/proxy/jdk/TestJdkProxyFactory.java
@@ -0,0 +1,12 @@
+package org.apache.commons.proxy.jdk;

+

+import org.apache.commons.proxy.AbstractProxyFactoryTestCase;

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

+

+public class TestJdkProxyFactory extends AbstractProxyFactoryTestCase

+{

+    public TestJdkProxyFactory()

+    {

+        super(new JdkProxyFactory());

+    }

+}

diff --git a/pom.xml b/pom.xml
index cc8596c..4cc21b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,13 @@
 <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>javassist</module>
+        <module>cglib</module>
+    </modules>
     <parent>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-parent</artifactId>
@@ -81,7 +88,6 @@
             <roles>
                 <role>advisor</role>
             </roles>
-            <url></url>
         </contributor>
         <contributor>
             <name>Howard M. Lewis Ship</name>
@@ -115,93 +121,6 @@
         </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.0,)</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>