This is an automated email from the ASF dual-hosted git repository.

svenmeier pushed a commit to branch WICKET-6913-wicket-9.x
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit 8287aa9f762a08574fe4ff433524aa9f374687fc
Author: Sven Meier <[email protected]>
AuthorDate: Mon Aug 23 15:02:31 2021 +0200

    WICKET-6913 bytebuddy for wicket-9.x
    
    optional via system parameter wicket.ioc.proxyfactory
---
 NOTICE                                             |   7 +-
 README.md                                          |   3 +-
 pom.xml                                            |   6 +
 wicket-examples/src/main/resources/META-INF/NOTICE |   9 +-
 wicket-ioc/pom.xml                                 |   6 +-
 wicket-ioc/src/main/java/module-info.java          |   1 +
 .../apache/wicket/proxy/IProxyFactory.java}        |  25 +-
 .../apache/wicket/proxy/IProxyTargetLocator.java   |   3 +-
 .../apache/wicket/proxy/LazyInitProxyFactory.java  | 434 ++++++---------------
 .../proxy/bytebuddy/ByteBuddyProxyFactory.java     | 302 ++++++++++++++
 .../ObjenesisByteBuddyInterceptor.java}            |  11 +-
 .../ObjenesisProxyFactory.java}                    |  25 +-
 .../ObjenesisProxyReplacement.java                 |   5 +-
 .../wicket/proxy/cglib/CglibProxyFactory.java      | 155 ++++++++
 .../apache/wicket/proxy/jdk/JdkProxyFactory.java   | 196 ++++++++++
 .../proxy/objenesis/ObjenesisCGLibInterceptor.java |   4 +-
 .../proxy/objenesis/ObjenesisProxyReplacement.java |   4 +-
 .../wicket/injection/util/MockDependency.java      |   5 +-
 .../annot/SpringBeanWithGenericsTest.java          |   2 +-
 19 files changed, 832 insertions(+), 371 deletions(-)

diff --git a/NOTICE b/NOTICE
index 3e462db..bc97306 100644
--- a/NOTICE
+++ b/NOTICE
@@ -56,8 +56,11 @@ src/./wicket-examples
    This product includes software developed by the CGLib Project
    (http://cglib.sourceforge.net).
 
-   This product includes ASM, released under a BSD style license 
(http://asm.objectweb.org).
-   Copyright (c) 2000-2005 INRIA, France Telecom
+   This product includes ASM, released under a BSD style license 
(https://asm.ow2.io/).
+   Copyright (c) 2000-2011 INRIA, France Telecom
+
+   This product includes software developed by the ByteBuddy Project
+   (https://bytebuddy.net/).
 
    This product includes jhighlight (https://jhighlight.dev.java.net/)
    which is released under CDDL 1.0 license 
(http://www.opensource.org/licenses/cddl1.php).
diff --git a/README.md b/README.md
index 01e9982..8faa041 100644
--- a/README.md
+++ b/README.md
@@ -159,7 +159,8 @@ the src/ folder.
  - wicket-ioc:
 
     cglib 3.1 (http://cglib.sourceforge.net/) and 
-    asm-util 5.0.3 (http://asm.objectweb.org/)
+    asm-util 9.1 (https://asm.ow2.io/)
+    byte-buddy 1.11.12 (https://bytebuddy.net/) and 
 
  - wicket-spring:
 
diff --git a/pom.xml b/pom.xml
index b4cbc76..589c2a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -139,6 +139,7 @@
                <assertj-core.version>3.19.0</assertj-core.version>
                <cdi-unit.version>4.1.0</cdi-unit.version>
                <cglib.version>3.3.0</cglib.version>
+               <byte-buddy.version>1.11.12</byte-buddy.version>
                <commons-collections.version>3.2.2</commons-collections.version>
                <commons-collections4.version>4.4</commons-collections4.version>
                <commons-fileupload.version>1.4</commons-fileupload.version>
@@ -321,6 +322,11 @@
                                <optional>true</optional>
                        </dependency>
                        <dependency>
+                               <groupId>net.bytebuddy</groupId>
+                               <artifactId>byte-buddy</artifactId>
+                               <version>${byte-buddy.version}</version>
+                       </dependency>
+                       <dependency>
                                <groupId>org.apache.commons</groupId>
                                <artifactId>commons-collections4</artifactId>
                                
<version>${commons-collections4.version}</version>
diff --git a/wicket-examples/src/main/resources/META-INF/NOTICE 
b/wicket-examples/src/main/resources/META-INF/NOTICE
index 619dd91..3598cb3 100644
--- a/wicket-examples/src/main/resources/META-INF/NOTICE
+++ b/wicket-examples/src/main/resources/META-INF/NOTICE
@@ -29,8 +29,11 @@
    This product includes software developed by the CGLib Project
    (http://cglib.sourceforge.net).
 
-   This product includes ASM, released under a BSD style license 
(http://asm.objectweb.org).
-   Copyright (c) 2000-2005 INRIA, France Telecom
+   This product includes ASM, released under a BSD style license 
(https://asm.ow2.io/).
+   Copyright (c) 2000-2011 INRIA, France Telecom
+
+   This product includes software developed by the ByteBuddy Project
+   (https://bytebuddy.net/).
 
    This product includes jhighlight (https://jhighlight.dev.java.net/)
    which is released under CDDL 1.0 license 
(http://www.opensource.org/licenses/cddl1.php).
@@ -42,4 +45,4 @@
    jQuery Foundation, Inc, http://jquery.org/license
 
    Contains qunit.css released under a MIT style license.
-   jQuery Foundation, Inc, http://jquery.org/license
\ No newline at end of file
+   jQuery Foundation, Inc, http://jquery.org/license
diff --git a/wicket-ioc/pom.xml b/wicket-ioc/pom.xml
index d34c07f..8ccb684 100644
--- a/wicket-ioc/pom.xml
+++ b/wicket-ioc/pom.xml
@@ -41,9 +41,13 @@
                        <artifactId>javax.inject</artifactId>
                </dependency>
                <dependency>
+                       <groupId>net.bytebuddy</groupId>
+                       <artifactId>byte-buddy</artifactId>
+                       <optional>true</optional>
+               </dependency>
+               <dependency>
                        <groupId>org.apache.wicket</groupId>
                        <artifactId>wicket-core</artifactId>
-                       <version>${project.version}</version>
                </dependency>
                <dependency>
                        <groupId>org.objenesis</groupId>
diff --git a/wicket-ioc/src/main/java/module-info.java 
b/wicket-ioc/src/main/java/module-info.java
index 6f7f7e1..d175804 100644
--- a/wicket-ioc/src/main/java/module-info.java
+++ b/wicket-ioc/src/main/java/module-info.java
@@ -19,6 +19,7 @@ module org.apache.wicket.ioc {
     requires org.apache.wicket.util;
     requires org.apache.wicket.core;
     requires cglib;
+    requires net.bytebuddy;
     requires org.objenesis;
 
     exports org.apache.wicket.injection;
diff --git a/wicket-ioc/src/main/java/module-info.java 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.java
similarity index 70%
copy from wicket-ioc/src/main/java/module-info.java
copy to wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.java
index 6f7f7e1..ef5f638 100644
--- a/wicket-ioc/src/main/java/module-info.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.java
@@ -14,14 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.wicket.proxy;
 
-module org.apache.wicket.ioc {
-    requires org.apache.wicket.util;
-    requires org.apache.wicket.core;
-    requires cglib;
-    requires org.objenesis;
-
-    exports org.apache.wicket.injection;
-    exports org.apache.wicket.proxy;
-    exports org.apache.wicket.proxy.objenesis;
+/**
+ * A factory of proxies.
+ */
+public interface IProxyFactory
+{
+       /**
+        * Create a proxy.
+        * 
+        * @param type
+        *            the target type
+        * @param locator
+        *            the locator of the target
+        * @return a proxy
+        */
+       public Object createProxy(final Class<?> type, final 
IProxyTargetLocator locator);
 }
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java
index 2b8eb25..6df597e 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.proxy;
 
+import org.apache.wicket.proxy.cglib.CglibProxyFactory;
 import org.apache.wicket.util.io.IClusterable;
 
 /**
@@ -43,7 +44,7 @@ import org.apache.wicket.util.io.IClusterable;
  * }
  * </pre>
  * 
- * @see LazyInitProxyFactory#createProxy(Class, IProxyTargetLocator)
+ * @see CglibProxyFactory#createProxy(Class, IProxyTargetLocator)
  * 
  * @author Igor Vaynberg (ivaynberg)
  * 
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java
index ccc7769..dc1ae95 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java
@@ -18,32 +18,23 @@ package org.apache.wicket.proxy;
 
 import java.io.ObjectStreamException;
 import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Proxy;
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.core.util.lang.WicketObjects;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.proxy.cglib.CglibProxyFactory;
+import org.apache.wicket.proxy.jdk.JdkProxyFactory;
+import org.apache.wicket.util.io.IClusterable;
+
 import net.sf.cglib.core.DefaultNamingPolicy;
 import net.sf.cglib.core.Predicate;
-import net.sf.cglib.proxy.Callback;
-import net.sf.cglib.proxy.CallbackFilter;
-import net.sf.cglib.proxy.Enhancer;
 import net.sf.cglib.proxy.MethodInterceptor;
 import net.sf.cglib.proxy.MethodProxy;
 import net.sf.cglib.proxy.NoOp;
 
-import org.apache.wicket.Application;
-import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.core.util.lang.WicketObjects;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.proxy.objenesis.ObjenesisProxyFactory;
-import org.apache.wicket.util.io.IClusterable;
-import org.apache.wicket.util.string.Strings;
-
 /**
  * A factory class that creates lazy init proxies given a type and a {@link 
IProxyTargetLocator}
  * used to retrieve the object the proxy will represent.
@@ -53,7 +44,7 @@ import org.apache.wicket.util.string.Strings;
  * forwarded.
  * <p>
  * This factory creates two kinds of proxies: A standard dynamic proxy when 
the specified type is an
- * interface, and a CGLib proxy when the specified type is a concrete class.
+ * interface, and a ByteBuddy proxy when the specified type is a concrete 
class.
  * <p>
  * The general use case for such a proxy is to represent a dependency that 
should not be serialized
  * with a wicket page or {@link IModel}. The solution is to serialize the 
proxy and the
@@ -112,16 +103,29 @@ public class LazyInitProxyFactory
        /**
         * Primitive java types and their object wrappers
         */
-       @SuppressWarnings({ "unchecked", "rawtypes" })
-       private static final List PRIMITIVES = Arrays.asList(String.class, 
byte.class, Byte.class,
+       private static final List<Class<?>> PRIMITIVES = 
Arrays.asList(String.class, byte.class, Byte.class,
                short.class, Short.class, int.class, Integer.class, long.class, 
Long.class, float.class,
                Float.class, double.class, Double.class, char.class, 
Character.class, boolean.class,
                Boolean.class);
-
-       private static final int CGLIB_CALLBACK_NO_OVERRIDE = 0;
-       private static final int CGLIB_CALLBACK_HANDLER = 1;
-
-       private static final boolean IS_OBJENESIS_AVAILABLE = 
isObjenesisAvailable();
+       
+       private static final IProxyFactory proxyFactory = initProxyFactory();
+       
+    private static IProxyFactory initProxyFactory() {
+               IProxyFactory proxyFactory = null; 
+               
+               String factoryName = 
System.getProperty("wicket.ioc.proxyfactory");
+               if (factoryName == null) {
+                       proxyFactory = new CglibProxyFactory();
+               } else {
+                       try {
+                               proxyFactory = (IProxyFactory) 
Class.forName(factoryName).getConstructor().newInstance();
+                       } catch (Exception e) {
+                               throw new 
Error(String.format("wicket.ioc.proxyFactory=%s", factoryName), e);
+                       }
+               }
+               
+               return proxyFactory;
+       }
 
        /**
         * Create a lazy init proxy for the specified type. The target object 
will be located using the
@@ -145,70 +149,14 @@ public class LazyInitProxyFactory
                }
                else if (type.isInterface())
                {
-                       JdkHandler handler = new JdkHandler(type, locator);
-
-                       try
-                       {
-                               return 
Proxy.newProxyInstance(resolveClassLoader(),
-                                       new Class[] { type, Serializable.class, 
ILazyInitProxy.class,
-                                                       IWriteReplace.class }, 
handler);
-                       }
-                       catch (IllegalArgumentException e)
-                       {
-                               /*
-                                * STW: In some clustering environments it 
appears the context classloader fails to
-                                * load the proxied interface (currently seen 
in BEA WLS 9.x clusters). If this
-                                * happens, we can try and fall back to the 
classloader (current) that actually
-                                * loaded this class.
-                                */
-                               return 
Proxy.newProxyInstance(LazyInitProxyFactory.class.getClassLoader(),
-                                       new Class[] { type, Serializable.class, 
ILazyInitProxy.class,
-                                                       IWriteReplace.class }, 
handler);
-                       }
-
-               }
-               else if (IS_OBJENESIS_AVAILABLE && !hasNoArgConstructor(type))
-               {
-                       return ObjenesisProxyFactory.createProxy(type, locator, 
WicketNamingPolicy.INSTANCE);
-               }
-               else
-               {
-                       CGLibInterceptor handler = new CGLibInterceptor(type, 
locator);
-
-                       Callback[] callbacks = new Callback[2];
-                       callbacks[CGLIB_CALLBACK_NO_OVERRIDE] = 
SerializableNoOpCallback.INSTANCE;
-                       callbacks[CGLIB_CALLBACK_HANDLER] = handler;
-
-                       Enhancer e = new Enhancer();
-                       e.setClassLoader(resolveClassLoader());
-                       e.setInterfaces(new Class[] { Serializable.class, 
ILazyInitProxy.class,
-                                       IWriteReplace.class });
-                       e.setSuperclass(type);
-                       
e.setCallbackFilter(NoOpForProtectedMethodsCGLibCallbackFilter.INSTANCE);
-                       e.setCallbacks(callbacks);
-                       e.setNamingPolicy(WicketNamingPolicy.INSTANCE);
-
-                       return e.create();
-               }
-       }
-
-       private static ClassLoader resolveClassLoader()
-       {
-               ClassLoader classLoader = null;
-               if (Application.exists())
-               {
-                       classLoader = Application.get().getApplicationSettings()
-                                       .getClassResolver().getClassLoader();
+                       return new JdkProxyFactory().createProxy(type, locator);
                }
-
-               if (classLoader == null) {
-                       classLoader = 
Thread.currentThread().getContextClassLoader();
+               else {
+                       return proxyFactory.createProxy(type, locator);
                }
-
-               return classLoader;
        }
-
-    /**
+       
+       /**
         * This interface is used to make the proxy forward writeReplace() call 
to the handler instead
         * of invoking it on itself. This allows us to serialize the 
replacement object instead of the
         * proxy itself in case the proxy subclass is deserialized on a VM that 
does not have it
@@ -229,7 +177,7 @@ public class LazyInitProxyFactory
                 */
                Object writeReplace() throws ObjectStreamException;
        }
-
+       
        /**
         * Object that replaces the proxy when it is serialized. Upon 
deserialization this object will
         * create a new proxy with the same locator.
@@ -237,7 +185,7 @@ public class LazyInitProxyFactory
         * @author Igor Vaynberg (ivaynberg)
         * 
         */
-       static class ProxyReplacement implements IClusterable
+       public static class ProxyReplacement implements IClusterable
        {
                private static final long serialVersionUID = 1L;
 
@@ -286,6 +234,72 @@ public class LazyInitProxyFactory
        }
 
        /**
+        * Checks if the method is derived from Object.equals()
+        * 
+        * @param method
+        *            method being tested
+        * @return true if the method is derived from Object.equals(), false 
otherwise
+        */
+       public static boolean isEqualsMethod(final Method method)
+       {
+               return (method.getReturnType() == boolean.class) &&
+                       (method.getParameterTypes().length == 1) &&
+                       (method.getParameterTypes()[0] == Object.class) && 
method.getName().equals("equals");
+       }
+
+       /**
+        * Checks if the method is derived from Object.hashCode()
+        * 
+        * @param method
+        *            method being tested
+        * @return true if the method is defined from Object.hashCode(), false 
otherwise
+        */
+       public static boolean isHashCodeMethod(final Method method)
+       {
+               return (method.getReturnType() == int.class) && 
(method.getParameterTypes().length == 0) &&
+                       method.getName().equals("hashCode");
+       }
+
+       /**
+        * Checks if the method is derived from Object.toString()
+        * 
+        * @param method
+        *            method being tested
+        * @return true if the method is defined from Object.toString(), false 
otherwise
+        */
+       public static boolean isToStringMethod(final Method method)
+       {
+               return (method.getReturnType() == String.class) &&
+                       (method.getParameterTypes().length == 0) && 
method.getName().equals("toString");
+       }
+
+       /**
+        * Checks if the method is derived from Object.finalize()
+        * 
+        * @param method
+        *            method being tested
+        * @return true if the method is defined from Object.finalize(), false 
otherwise
+        */
+       public static boolean isFinalizeMethod(final Method method)
+       {
+               return (method.getReturnType() == void.class) && 
(method.getParameterTypes().length == 0) &&
+                       method.getName().equals("finalize");
+       }
+
+       /**
+        * Checks if the method is the writeReplace method
+        * 
+        * @param method
+        *            method being tested
+        * @return true if the method is the writeReplace method, false 
otherwise
+        */
+       public static boolean isWriteReplaceMethod(final Method method)
+       {
+               return (method.getReturnType() == Object.class) &&
+                       (method.getParameterTypes().length == 0) && 
method.getName().equals("writeReplace");
+       }
+
+       /**
         * Method interceptor for proxies representing concrete object not 
backed by an interface. These
         * proxies are represented by cglib proxies.
         * 
@@ -331,24 +345,24 @@ public class LazyInitProxyFactory
                public Object intercept(final Object object, final Method 
method, final Object[] args,
                        final MethodProxy proxy) throws Throwable
                {
-                       if (isFinalizeMethod(method))
+                       if (LazyInitProxyFactory.isFinalizeMethod(method))
                        {
                                // swallow finalize call
                                return null;
                        }
-                       else if (isEqualsMethod(method))
+                       else if (LazyInitProxyFactory.isEqualsMethod(method))
                        {
                                return (equals(args[0])) ? Boolean.TRUE : 
Boolean.FALSE;
                        }
-                       else if (isHashCodeMethod(method))
+                       else if (LazyInitProxyFactory.isHashCodeMethod(method))
                        {
                                return hashCode();
                        }
-                       else if (isToStringMethod(method))
+                       else if (LazyInitProxyFactory.isToStringMethod(method))
                        {
                                return toString();
                        }
-                       else if (isWriteReplaceMethod(method))
+                       else if 
(LazyInitProxyFactory.isWriteReplaceMethod(method))
                        {
                                return writeReplace();
                        }
@@ -380,16 +394,13 @@ public class LazyInitProxyFactory
         *
         * @author Igor Vaynberg (ivaynberg)
         */
-       protected static class CGLibInterceptor extends AbstractCGLibInterceptor
+       public static class CGLibInterceptor extends AbstractCGLibInterceptor
        {
                public CGLibInterceptor(Class<?> type, IProxyTargetLocator 
locator)
                {
                        super(type, locator);
                }
 
-               /**
-                * @see 
org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace#writeReplace()
-                */
                @Override
                public Object writeReplace() throws ObjectStreamException
                {
@@ -404,214 +415,10 @@ public class LazyInitProxyFactory
        {
                private static final long serialVersionUID = 1L;
 
-               private static final NoOp INSTANCE = new 
SerializableNoOpCallback();
-       }
-
-       /**
-        * CGLib callback filter which does not intercept protected methods.
-        * 
-        * Protected methods need to be called with invokeSuper() instead of 
invoke().
-        * When invoke() is called on a protected method, it throws an 
"IllegalArgumentException:
-        * Protected method" exception.
-        * That being said, we do not need to intercept the protected methods 
so this callback filter
-        * is designed to use a NoOp callback for protected methods.
-        * 
-        * @see <a 
href="http://comments.gmane.org/gmane.comp.java.cglib.devel/720";>Discussion 
about
-        * this very issue in Spring AOP</a>
-        * @see <a 
href="https://github.com/wicketstuff/core/wiki/SpringReference";>The WicketStuff
-        * SpringReference project which worked around this issue</a>
-        */
-       private static class NoOpForProtectedMethodsCGLibCallbackFilter 
implements CallbackFilter
-       {
-               private static final CallbackFilter INSTANCE = new 
NoOpForProtectedMethodsCGLibCallbackFilter();
-
-               @Override
-               public int accept(Method method) {
-                       if (Modifier.isProtected(method.getModifiers()))
-                       {
-                               return CGLIB_CALLBACK_NO_OVERRIDE;
-                       }
-                       else
-                       {
-                               return CGLIB_CALLBACK_HANDLER;
-                       }
-               }
-       }
-
-       /**
-        * Invocation handler for proxies representing interface based object. 
For interface backed
-        * objects dynamic jdk proxies are used.
-        * 
-        * @author Igor Vaynberg (ivaynberg)
-        * 
-        */
-       private static class JdkHandler
-               implements
-                       InvocationHandler,
-                       ILazyInitProxy,
-                       Serializable,
-                       IWriteReplace
-       {
-               private static final long serialVersionUID = 1L;
-
-               private final IProxyTargetLocator locator;
-
-               private final String typeName;
-
-               private transient Object target;
-
-               /**
-                * Constructor
-                * 
-                * @param type
-                *            class of object this handler will represent
-                * 
-                * @param locator
-                *            object locator used to locate the object this 
proxy represents
-                */
-               public JdkHandler(final Class<?> type, final 
IProxyTargetLocator locator)
-               {
-                       super();
-                       this.locator = locator;
-                       typeName = type.getName();
-               }
-
-               /**
-                * @see 
java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
-                *      java.lang.reflect.Method, java.lang.Object[])
-                */
-               @Override
-               public Object invoke(final Object proxy, final Method method, 
final Object[] args)
-                       throws Throwable
-               {
-                       if (isFinalizeMethod(method))
-                       {
-                               // swallow finalize call
-                               return null;
-                       }
-                       else if (isEqualsMethod(method))
-                       {
-                               return (equals(args[0])) ? Boolean.TRUE : 
Boolean.FALSE;
-                       }
-                       else if (isHashCodeMethod(method))
-                       {
-                               return hashCode();
-                       }
-                       else if (isToStringMethod(method))
-                       {
-                               return toString();
-                       }
-                       else if 
(method.getDeclaringClass().equals(ILazyInitProxy.class))
-                       {
-                               return getObjectLocator();
-                       }
-                       else if (isWriteReplaceMethod(method))
-                       {
-                               return writeReplace();
-                       }
-
-                       if (target == null)
-                       {
-
-                               target = locator.locateProxyTarget();
-                       }
-                       try
-                       {
-                               method.setAccessible(true);
-                               return method.invoke(target, args);
-                       }
-                       catch (InvocationTargetException e)
-                       {
-                               throw e.getTargetException();
-                       }
-               }
-
-               /**
-                * @see 
org.apache.wicket.proxy.ILazyInitProxy#getObjectLocator()
-                */
-               @Override
-               public IProxyTargetLocator getObjectLocator()
-               {
-                       return locator;
-               }
-
-               /**
-                * @see 
org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace#writeReplace()
-                */
-               @Override
-               public Object writeReplace() throws ObjectStreamException
-               {
-                       return new ProxyReplacement(typeName, locator);
-               }
+               public static final NoOp INSTANCE = new 
SerializableNoOpCallback();
        }
-
-       /**
-        * Checks if the method is derived from Object.equals()
-        * 
-        * @param method
-        *            method being tested
-        * @return true if the method is derived from Object.equals(), false 
otherwise
-        */
-       public static boolean isEqualsMethod(final Method method)
-       {
-               return (method.getReturnType() == boolean.class) &&
-                       (method.getParameterTypes().length == 1) &&
-                       (method.getParameterTypes()[0] == Object.class) && 
method.getName().equals("equals");
-       }
-
-       /**
-        * Checks if the method is derived from Object.hashCode()
-        * 
-        * @param method
-        *            method being tested
-        * @return true if the method is defined from Object.hashCode(), false 
otherwise
-        */
-       public static boolean isHashCodeMethod(final Method method)
-       {
-               return (method.getReturnType() == int.class) && 
(method.getParameterTypes().length == 0) &&
-                       method.getName().equals("hashCode");
-       }
-
-       /**
-        * Checks if the method is derived from Object.toString()
-        * 
-        * @param method
-        *            method being tested
-        * @return true if the method is defined from Object.toString(), false 
otherwise
-        */
-       public static boolean isToStringMethod(final Method method)
-       {
-               return (method.getReturnType() == String.class) &&
-                       (method.getParameterTypes().length == 0) && 
method.getName().equals("toString");
-       }
-
-       /**
-        * Checks if the method is derived from Object.finalize()
-        * 
-        * @param method
-        *            method being tested
-        * @return true if the method is defined from Object.finalize(), false 
otherwise
-        */
-       public static boolean isFinalizeMethod(final Method method)
-       {
-               return (method.getReturnType() == void.class) && 
(method.getParameterTypes().length == 0) &&
-                       method.getName().equals("finalize");
-       }
-
-       /**
-        * Checks if the method is the writeReplace method
-        * 
-        * @param method
-        *            method being tested
-        * @return true if the method is the writeReplace method, false 
otherwise
-        */
-       public static boolean isWriteReplaceMethod(final Method method)
-       {
-               return (method.getReturnType() == Object.class) &&
-                       (method.getParameterTypes().length == 0) && 
method.getName().equals("writeReplace");
-       }
-
-       public static final class WicketNamingPolicy extends DefaultNamingPolicy
+       
+       public static class WicketNamingPolicy extends DefaultNamingPolicy
        {
                public static final WicketNamingPolicy INSTANCE = new 
WicketNamingPolicy();
 
@@ -632,25 +439,4 @@ public class LazyInitProxyFactory
                }
        }
 
-
-       private static boolean hasNoArgConstructor(Class<?> type)
-       {
-               for (Constructor<?> constructor : 
type.getDeclaredConstructors())
-               {
-                       if (constructor.getParameterTypes().length == 0)
-                               return true;
-               }
-
-               return false;
-       }
-
-       private static boolean isObjenesisAvailable()
-       {
-               try {
-                       Class.forName("org.objenesis.ObjenesisStd");
-                       return true;
-               } catch (Exception ignored) {
-                       return false;
-               }
-       }
 }
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ByteBuddyProxyFactory.java
 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ByteBuddyProxyFactory.java
new file mode 100644
index 0000000..3274d01
--- /dev/null
+++ 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ByteBuddyProxyFactory.java
@@ -0,0 +1,302 @@
+/*
+ * 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.wicket.proxy.bytebuddy;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.function.Function;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.proxy.ILazyInitProxy;
+import org.apache.wicket.proxy.IProxyFactory;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace;
+import org.apache.wicket.proxy.LazyInitProxyFactory.ProxyReplacement;
+
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.NamingStrategy;
+import net.bytebuddy.TypeCache;
+import net.bytebuddy.description.modifier.Visibility;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
+import net.bytebuddy.implementation.FieldAccessor;
+import net.bytebuddy.implementation.MethodDelegation;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.Pipe;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.matcher.ElementMatchers;
+
+/**
+ * A factory class that creates bytebuddy proxies.
+ */
+public class ByteBuddyProxyFactory implements IProxyFactory
+{
+       /**
+        * A cache used to store the dynamically generated classes by ByteBuddy.
+        * Without this cache a new class will be generated for each proxy 
creation
+        * and this will fill up the metaspace
+        */
+       private static final TypeCache<TypeCache.SimpleKey> DYNAMIC_CLASS_CACHE 
= new TypeCache.WithInlineExpunction<>(TypeCache.Sort.SOFT);
+
+       private static final ByteBuddy BYTE_BUDDY = new 
ByteBuddy().with(WicketNamingStrategy.INSTANCE);
+
+       private static final boolean IS_OBJENESIS_AVAILABLE = 
isObjenesisAvailable();
+
+       /**
+        * Create a lazy init proxy for the specified type. The target object 
will be located using the
+        * provided locator upon first method invocation.
+        * 
+        * @param type
+        *            type that proxy will represent
+        * 
+        * @param locator
+        *            object locator that will locate the object the proxy 
represents
+        * 
+        * @return lazily initializable proxy
+        */
+       @Override
+       public Object createProxy(final Class<?> type, final 
IProxyTargetLocator locator)
+       {
+               if (IS_OBJENESIS_AVAILABLE && !hasNoArgConstructor(type))
+               {
+                       return ObjenesisProxyFactory.createProxy(type, locator);
+               }
+               else
+               {
+                       Class<?> proxyClass = createOrGetProxyClass(type);
+
+                       try
+                       {
+                               Object instance = 
proxyClass.getDeclaredConstructor().newInstance();
+                               ByteBuddyInterceptor interceptor = new 
ByteBuddyInterceptor(type, locator);
+                               ((InterceptorMutator) 
instance).setInterceptor(interceptor);
+                               return instance;
+                       }
+                       catch (InstantiationException | IllegalAccessException 
| InvocationTargetException | NoSuchMethodException e)
+                       {
+                               throw new WicketRuntimeException(e);
+                       }
+               }
+       }
+
+       public static Class<?> createOrGetProxyClass(Class<?> type)
+       {
+               ClassLoader classLoader = resolveClassLoader();
+               return DYNAMIC_CLASS_CACHE.findOrInsert(classLoader,
+                               new TypeCache.SimpleKey(type),
+                               () -> BYTE_BUDDY
+                                               .subclass(type)
+                                               
.method(ElementMatchers.isPublic())
+                                                       .intercept(
+                                                               MethodDelegation
+                                                                       
.withDefaultConfiguration()
+                                                                       
.withBinders(Pipe.Binder.install(Function.class))
+                                                                       
.toField("interceptor"))
+                                               .defineField("interceptor", 
ByteBuddyInterceptor.class, Visibility.PRIVATE)
+                                               
.implement(InterceptorMutator.class).intercept(FieldAccessor.ofBeanProperty())
+                                               .implement(Serializable.class, 
IWriteReplace.class, 
ILazyInitProxy.class).intercept(MethodDelegation.toField("interceptor"))
+                                               .make()
+                                               .load(classLoader, 
ClassLoadingStrategy.Default.INJECTION)
+                                               .getLoaded());
+       }
+
+       private static ClassLoader resolveClassLoader()
+       {
+               ClassLoader classLoader = null;
+               if (Application.exists())
+               {
+                       classLoader = Application.get().getApplicationSettings()
+                                       .getClassResolver().getClassLoader();
+               }
+
+               if (classLoader == null) {
+                       classLoader = 
Thread.currentThread().getContextClassLoader();
+               }
+
+               return classLoader;
+       }
+
+       /**
+        * An interface used to set the Byte Buddy interceptor after creating an
+        * instance of the dynamically created proxy class.
+        * We need to set the interceptor as a field in the proxy class so that
+        * we could use different interceptors for proxied classes with 
generics.
+        * For example: a {@link org.apache.wicket.Component} may need to inject
+        * two beans with the same raw type but different generic type(s) (<em>
+        * ArrayList&lt;String&gt;</em> and <em>ArrayList&lt;Integer&gt;</em>).
+        * Since the generic types are erased at runtime, and we use caching 
for the
+        * dynamic proxy classes we need to be able to set different 
interceptors
+        * after instantiating the proxy class.
+        */
+       public interface InterceptorMutator
+       {
+               void setInterceptor(ByteBuddyInterceptor interceptor);
+       }
+
+       /**
+        * Method interceptor for proxies representing concrete object not 
backed by an interface.
+        * These proxies are represented by ByteBuddy proxies.
+        * 
+        * @author Igor Vaynberg (ivaynberg)
+        */
+       public static class ByteBuddyInterceptor
+               implements
+                       ILazyInitProxy,
+                       Serializable,
+                       IWriteReplace
+       {
+               private static final long serialVersionUID = 1L;
+
+               protected final IProxyTargetLocator locator;
+
+               protected final String typeName;
+
+               private transient Object target;
+
+               /**
+                * Constructor
+                * 
+                * @param type
+                *            class of the object this proxy was created for
+                * 
+                * @param locator
+                *            object locator used to locate the object this 
proxy represents
+                */
+               public ByteBuddyInterceptor(final Class<?> type, final 
IProxyTargetLocator locator)
+               {
+                       super();
+                       
+                       this.typeName = type.getName();
+                       this.locator = locator;
+               }
+
+               @RuntimeType
+               public Object intercept(@Origin Method method, @AllArguments 
Object[] args, @Pipe Function pipe) throws Exception
+               {
+                       if (LazyInitProxyFactory.isFinalizeMethod(method))
+                       {
+                               // swallow finalize call
+                               return null;
+                       }
+                       else if (LazyInitProxyFactory.isEqualsMethod(method))
+                       {
+                               return (equals(args[0])) ? Boolean.TRUE : 
Boolean.FALSE;
+                       }
+                       else if (LazyInitProxyFactory.isHashCodeMethod(method))
+                       {
+                               return hashCode();
+                       }
+                       else if (LazyInitProxyFactory.isToStringMethod(method))
+                       {
+                               return toString();
+                       }
+                       
+                       if (target == null)
+                       {
+                               target = locator.locateProxyTarget();
+                       }
+                       
+                       return pipe.apply(target);
+               }
+
+               @Override
+               public IProxyTargetLocator getObjectLocator()
+               {
+                       return locator;
+               }
+
+               @Override
+               public Object writeReplace() throws ObjectStreamException
+               {
+                       return new ProxyReplacement(typeName, locator);
+               }
+       }
+
+       /**
+        * A strategy that decides what should be the fully qualified name of 
the generated
+        * classes. Since it is not possible to create new classes in the 
<em>java.**</em>
+        * package we modify the package name by prefixing it with 
<em>bytebuddy_generated_wicket_proxy.</em>.
+        * For classes in any other packages we modify just the class name by 
prefixing
+        * it with <em>WicketProxy_</em>. This way the generated proxy class 
could still
+        * access package-private members of sibling classes.
+        */
+       public static final class WicketNamingStrategy extends 
NamingStrategy.AbstractBase
+       {
+               public static final WicketNamingStrategy INSTANCE = new 
WicketNamingStrategy();
+
+               private WicketNamingStrategy()
+               {
+                       super();
+               }
+
+               @Override
+               protected String name(TypeDescription superClass) {
+                       String prefix = superClass.getName();
+                       int lastIdxOfDot = prefix.lastIndexOf('.');
+                       String packageName = prefix.substring(0, lastIdxOfDot);
+                       String className = prefix.substring(lastIdxOfDot + 1);
+                       String name = packageName + ".";
+                       if (prefix.startsWith("java."))
+                       {
+                               name = "bytebuddy_generated_wicket_proxy." + 
name + className;
+                       }
+                       else
+                       {
+                               name += "WicketProxy_" + className;
+                       }
+                       return name;
+               }
+
+               @Override
+               public String redefine(TypeDescription typeDescription) {
+                       return typeDescription.getName();
+               }
+
+               @Override
+               public String rebase(TypeDescription typeDescription) {
+                       return typeDescription.getName();
+               }
+       }
+
+
+       private static boolean hasNoArgConstructor(Class<?> type)
+       {
+               for (Constructor<?> constructor : 
type.getDeclaredConstructors())
+               {
+                       if (constructor.getParameterTypes().length == 0)
+                               return true;
+               }
+
+               return false;
+       }
+
+       private static boolean isObjenesisAvailable()
+       {
+               try {
+                       Class.forName("org.objenesis.ObjenesisStd");
+                       return true;
+               } catch (Exception ignored) {
+                       return false;
+               }
+       }
+}
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
similarity index 76%
copy from 
wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
copy to 
wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
index 63975a0..bac1b64 100644
--- 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
+++ 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
@@ -14,20 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.wicket.proxy.objenesis;
+package org.apache.wicket.proxy.bytebuddy;
 
 import java.io.ObjectStreamException;
 
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
 
 /**
- * Method interceptor for proxies representing concrete object not backed by 
an interface. These
- * proxies are representing by cglib proxies.
+ * Method interceptor for proxies representing concrete object not backed by 
an interface.
+ * These proxies are representing by ByteBuddy proxies.
  */
-public class ObjenesisCGLibInterceptor extends 
LazyInitProxyFactory.AbstractCGLibInterceptor
+public class ObjenesisByteBuddyInterceptor extends 
ByteBuddyProxyFactory.ByteBuddyInterceptor
 {
-       public ObjenesisCGLibInterceptor(Class<?> type, IProxyTargetLocator 
locator) {
+       public ObjenesisByteBuddyInterceptor(Class<?> type, IProxyTargetLocator 
locator) {
                super(type, locator);
        }
 
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java
similarity index 58%
copy from 
wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
copy to 
wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java
index 63975a0..45cf623 100644
--- 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
+++ 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java
@@ -14,26 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.wicket.proxy.objenesis;
-
-import java.io.ObjectStreamException;
+package org.apache.wicket.proxy.bytebuddy;
 
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.objenesis.ObjenesisStd;
 
-/**
- * Method interceptor for proxies representing concrete object not backed by 
an interface. These
- * proxies are representing by cglib proxies.
- */
-public class ObjenesisCGLibInterceptor extends 
LazyInitProxyFactory.AbstractCGLibInterceptor
+public class ObjenesisProxyFactory
 {
-       public ObjenesisCGLibInterceptor(Class<?> type, IProxyTargetLocator 
locator) {
-               super(type, locator);
-       }
+       private static final ObjenesisStd OBJENESIS = new ObjenesisStd(false);
 
-       @Override
-       public Object writeReplace() throws ObjectStreamException
+       public static Object createProxy(final Class<?> type, final 
IProxyTargetLocator locator)
        {
-               return new ObjenesisProxyReplacement(typeName, locator);
+               Class<?> proxyClass = 
ByteBuddyProxyFactory.createOrGetProxyClass(type);
+               Object instance = OBJENESIS.newInstance(proxyClass);
+               ObjenesisByteBuddyInterceptor interceptor = new 
ObjenesisByteBuddyInterceptor(type, locator);
+               ((ByteBuddyProxyFactory.InterceptorMutator) 
instance).setInterceptor(interceptor);
+               return instance;
        }
 }
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
similarity index 90%
copy from 
wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
copy to 
wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
index cbffee8..4430182 100644
--- 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
+++ 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
@@ -14,14 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.wicket.proxy.objenesis;
+package org.apache.wicket.proxy.bytebuddy;
 
 import java.io.ObjectStreamException;
 
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.core.util.lang.WicketObjects;
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
 import org.apache.wicket.util.io.IClusterable;
 
 /**
@@ -50,6 +49,6 @@ class ObjenesisProxyReplacement implements IClusterable
                                                        "] with the currently 
configured org.apache.wicket.application.IClassResolver");
                        throw new WicketRuntimeException(cause);
                }
-               return ObjenesisProxyFactory.createProxy(clazz, locator, 
LazyInitProxyFactory.WicketNamingPolicy.INSTANCE);
+               return ObjenesisProxyFactory.createProxy(clazz, locator);
        }
 }
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/cglib/CglibProxyFactory.java 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/cglib/CglibProxyFactory.java
new file mode 100644
index 0000000..2cc8be7
--- /dev/null
+++ 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/cglib/CglibProxyFactory.java
@@ -0,0 +1,155 @@
+/*
+ * 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.wicket.proxy.cglib;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.proxy.ILazyInitProxy;
+import org.apache.wicket.proxy.IProxyFactory;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.proxy.LazyInitProxyFactory.CGLibInterceptor;
+import org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace;
+import org.apache.wicket.proxy.LazyInitProxyFactory.SerializableNoOpCallback;
+import org.apache.wicket.proxy.LazyInitProxyFactory.WicketNamingPolicy;
+import org.apache.wicket.proxy.objenesis.ObjenesisProxyFactory;
+
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.Enhancer;
+
+/**
+ * A factory class that creates cglib proxies.
+ */
+public class CglibProxyFactory implements IProxyFactory
+{
+       private static final int CGLIB_CALLBACK_NO_OVERRIDE = 0;
+       private static final int CGLIB_CALLBACK_HANDLER = 1;
+
+       private static final boolean IS_OBJENESIS_AVAILABLE = 
isObjenesisAvailable();
+
+       /**
+        * Create a lazy init proxy for the specified type. The target object 
will be located using the
+        * provided locator upon first method invocation.
+        * 
+        * @param type
+        *            type that proxy will represent
+        * 
+        * @param locator
+        *            object locator that will locate the object the proxy 
represents
+        * 
+        * @return lazily initializable proxy
+        */
+       @Override
+       public Object createProxy(final Class<?> type, final 
IProxyTargetLocator locator)
+       {
+               if (IS_OBJENESIS_AVAILABLE && !hasNoArgConstructor(type))
+               {
+                       return ObjenesisProxyFactory.createProxy(type, locator, 
WicketNamingPolicy.INSTANCE);
+               }
+               else
+               {
+                       CGLibInterceptor handler = new CGLibInterceptor(type, 
locator);
+
+                       Callback[] callbacks = new Callback[2];
+                       callbacks[CGLIB_CALLBACK_NO_OVERRIDE] = 
SerializableNoOpCallback.INSTANCE;
+                       callbacks[CGLIB_CALLBACK_HANDLER] = handler;
+
+                       Enhancer e = new Enhancer();
+                       e.setClassLoader(resolveClassLoader());
+                       e.setInterfaces(new Class[] { Serializable.class, 
ILazyInitProxy.class,
+                                       IWriteReplace.class });
+                       e.setSuperclass(type);
+                       
e.setCallbackFilter(NoOpForProtectedMethodsCGLibCallbackFilter.INSTANCE);
+                       e.setCallbacks(callbacks);
+                       e.setNamingPolicy(WicketNamingPolicy.INSTANCE);
+
+                       return e.create();
+               }
+       }
+
+       private static ClassLoader resolveClassLoader()
+       {
+               ClassLoader classLoader = null;
+               if (Application.exists())
+               {
+                       classLoader = Application.get().getApplicationSettings()
+                                       .getClassResolver().getClassLoader();
+               }
+
+               if (classLoader == null) {
+                       classLoader = 
Thread.currentThread().getContextClassLoader();
+               }
+
+               return classLoader;
+       }
+
+       /**
+        * CGLib callback filter which does not intercept protected methods.
+        * 
+        * Protected methods need to be called with invokeSuper() instead of 
invoke().
+        * When invoke() is called on a protected method, it throws an 
"IllegalArgumentException:
+        * Protected method" exception.
+        * That being said, we do not need to intercept the protected methods 
so this callback filter
+        * is designed to use a NoOp callback for protected methods.
+        * 
+        * @see <a 
href="http://comments.gmane.org/gmane.comp.java.cglib.devel/720";>Discussion 
about
+        * this very issue in Spring AOP</a>
+        * @see <a 
href="https://github.com/wicketstuff/core/wiki/SpringReference";>The WicketStuff
+        * SpringReference project which worked around this issue</a>
+        */
+       private static class NoOpForProtectedMethodsCGLibCallbackFilter 
implements CallbackFilter
+       {
+               private static final CallbackFilter INSTANCE = new 
NoOpForProtectedMethodsCGLibCallbackFilter();
+
+               @Override
+               public int accept(Method method) {
+                       if (Modifier.isProtected(method.getModifiers()))
+                       {
+                               return CGLIB_CALLBACK_NO_OVERRIDE;
+                       }
+                       else
+                       {
+                               return CGLIB_CALLBACK_HANDLER;
+                       }
+               }
+       }
+
+       private static boolean hasNoArgConstructor(Class<?> type)
+       {
+               for (Constructor<?> constructor : 
type.getDeclaredConstructors())
+               {
+                       if (constructor.getParameterTypes().length == 0)
+                               return true;
+               }
+
+               return false;
+       }
+
+       private static boolean isObjenesisAvailable()
+       {
+               try {
+                       Class.forName("org.objenesis.ObjenesisStd");
+                       return true;
+               } catch (Exception ignored) {
+                       return false;
+               }
+       }
+}
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/jdk/JdkProxyFactory.java 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/jdk/JdkProxyFactory.java
new file mode 100644
index 0000000..66ecda0
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/jdk/JdkProxyFactory.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.wicket.proxy.jdk;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.proxy.ILazyInitProxy;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace;
+import org.apache.wicket.proxy.LazyInitProxyFactory.ProxyReplacement;
+
+/**
+ * A factory class that creates jdk dynamic proxies.
+ */
+public class JdkProxyFactory
+{
+       /**
+        * Create a lazy init proxy for the specified type. The target object 
will be located using the
+        * provided locator upon first method invocation.
+        * 
+        * @param type
+        *            type that proxy will represent
+        * 
+        * @param locator
+        *            object locator that will locate the object the proxy 
represents
+        * 
+        * @return lazily initializable proxy
+        */
+       public Object createProxy(final Class<?> type, final 
IProxyTargetLocator locator)
+       {
+               JdkHandler handler = new JdkHandler(type, locator);
+
+               try
+               {
+                       return Proxy.newProxyInstance(resolveClassLoader(),
+                               new Class[] { type, Serializable.class, 
ILazyInitProxy.class,
+                                               IWriteReplace.class }, handler);
+               }
+               catch (IllegalArgumentException e)
+               {
+                       /*
+                        * STW: In some clustering environments it appears the 
context classloader fails to
+                        * load the proxied interface (currently seen in BEA 
WLS 9.x clusters). If this
+                        * happens, we can try and fall back to the classloader 
(current) that actually
+                        * loaded this class.
+                        */
+                       return 
Proxy.newProxyInstance(LazyInitProxyFactory.class.getClassLoader(),
+                               new Class[] { type, Serializable.class, 
ILazyInitProxy.class,
+                                               IWriteReplace.class }, handler);
+               }
+       }
+       
+       private ClassLoader resolveClassLoader()
+       {
+               ClassLoader classLoader = null;
+               if (Application.exists())
+               {
+                       classLoader = Application.get().getApplicationSettings()
+                                       .getClassResolver().getClassLoader();
+               }
+
+               if (classLoader == null) {
+                       classLoader = 
Thread.currentThread().getContextClassLoader();
+               }
+
+               return classLoader;
+       }
+
+       /**
+        * Invocation handler for proxies representing interface based object. 
For interface backed
+        * objects dynamic jdk proxies are used.
+        * 
+        * @author Igor Vaynberg (ivaynberg)
+        * 
+        */
+       private static class JdkHandler
+               implements
+                       InvocationHandler,
+                       ILazyInitProxy,
+                       Serializable,
+                       IWriteReplace
+       {
+               private static final long serialVersionUID = 1L;
+
+               private final IProxyTargetLocator locator;
+
+               private final String typeName;
+
+               private transient Object target;
+
+               /**
+                * Constructor
+                * 
+                * @param type
+                *            class of object this handler will represent
+                * 
+                * @param locator
+                *            object locator used to locate the object this 
proxy represents
+                */
+               public JdkHandler(final Class<?> type, final 
IProxyTargetLocator locator)
+               {
+                       super();
+                       this.locator = locator;
+                       typeName = type.getName();
+               }
+
+               /**
+                * @see 
java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
+                *      java.lang.reflect.Method, java.lang.Object[])
+                */
+               @Override
+               public Object invoke(final Object proxy, final Method method, 
final Object[] args)
+                       throws Throwable
+               {
+                       if (LazyInitProxyFactory.isFinalizeMethod(method))
+                       {
+                               // swallow finalize call
+                               return null;
+                       }
+                       else if (LazyInitProxyFactory.isEqualsMethod(method))
+                       {
+                               return (equals(args[0])) ? Boolean.TRUE : 
Boolean.FALSE;
+                       }
+                       else if (LazyInitProxyFactory.isHashCodeMethod(method))
+                       {
+                               return hashCode();
+                       }
+                       else if (LazyInitProxyFactory.isToStringMethod(method))
+                       {
+                               return toString();
+                       }
+                       else if 
(method.getDeclaringClass().equals(ILazyInitProxy.class))
+                       {
+                               return getObjectLocator();
+                       }
+                       else if 
(LazyInitProxyFactory.isWriteReplaceMethod(method))
+                       {
+                               return writeReplace();
+                       }
+
+                       if (target == null)
+                       {
+
+                               target = locator.locateProxyTarget();
+                       }
+                       try
+                       {
+                               method.setAccessible(true);
+                               return method.invoke(target, args);
+                       }
+                       catch (InvocationTargetException e)
+                       {
+                               throw e.getTargetException();
+                       }
+               }
+
+               /**
+                * @see 
org.apache.wicket.proxy.ILazyInitProxy#getObjectLocator()
+                */
+               @Override
+               public IProxyTargetLocator getObjectLocator()
+               {
+                       return locator;
+               }
+
+               /**
+                * @see 
org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace#writeReplace()
+                */
+               @Override
+               public Object writeReplace() throws ObjectStreamException
+               {
+                       return new ProxyReplacement(typeName, locator);
+               }
+       }
+}
\ No newline at end of file
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
index 63975a0..d21e011 100644
--- 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
+++ 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
@@ -19,13 +19,13 @@ package org.apache.wicket.proxy.objenesis;
 import java.io.ObjectStreamException;
 
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.AbstractCGLibInterceptor;
 
 /**
  * Method interceptor for proxies representing concrete object not backed by 
an interface. These
  * proxies are representing by cglib proxies.
  */
-public class ObjenesisCGLibInterceptor extends 
LazyInitProxyFactory.AbstractCGLibInterceptor
+public class ObjenesisCGLibInterceptor extends AbstractCGLibInterceptor
 {
        public ObjenesisCGLibInterceptor(Class<?> type, IProxyTargetLocator 
locator) {
                super(type, locator);
diff --git 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
index cbffee8..fc8f22d 100644
--- 
a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
+++ 
b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
@@ -21,7 +21,7 @@ import java.io.ObjectStreamException;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.core.util.lang.WicketObjects;
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.WicketNamingPolicy;
 import org.apache.wicket.util.io.IClusterable;
 
 /**
@@ -50,6 +50,6 @@ class ObjenesisProxyReplacement implements IClusterable
                                                        "] with the currently 
configured org.apache.wicket.application.IClassResolver");
                        throw new WicketRuntimeException(cause);
                }
-               return ObjenesisProxyFactory.createProxy(clazz, locator, 
LazyInitProxyFactory.WicketNamingPolicy.INSTANCE);
+               return ObjenesisProxyFactory.createProxy(clazz, locator, 
WicketNamingPolicy.INSTANCE);
        }
 }
diff --git 
a/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java 
b/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java
index cc22135..6afeecc 100644
--- 
a/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java
+++ 
b/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java
@@ -16,6 +16,8 @@
  */
 package org.apache.wicket.injection.util;
 
+import org.apache.wicket.proxy.LazyInitProxyFactory;
+
 /**
  * Mock dependency that does not implement an interface
  * 
@@ -27,7 +29,8 @@ public class MockDependency
        private String message;
 
        /**
-        * Empty default constructor. It is required by cglib to create a proxy.
+        * Empty default constructor. It is required by {@link 
LazyInitProxyFactory}
+        * to create a proxy.
         */
        public MockDependency()
        {
diff --git 
a/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java
 
b/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java
index 653a93c..ac743c1 100644
--- 
a/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java
+++ 
b/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java
@@ -290,7 +290,7 @@ class SpringBeanWithGenericsTest
                @Bean
                public List<String> stringsList()
                {
-                       return Arrays.asList("foo", "bar", "baz");
+                       return List.of("foo", "bar", "baz");
                }
 
                @Bean

Reply via email to