WICKET-6913 bytebuddy for wicket-9.x
optional via system parameter wicket.ioc.proxyfactory
diff --git a/NOTICE b/NOTICE
index 3e462db..bc97306 100644
--- a/NOTICE
+++ b/NOTICE
@@ -56,8 +56,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).
diff --git a/README.md b/README.md
index 01e9982..8faa041 100644
--- a/README.md
+++ b/README.md
@@ -159,7 +159,8 @@
- 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 @@
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/org/apache/wicket/proxy/IProxyFactory.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.java
new file mode 100644
index 0000000..ef5f638
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.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.wicket.proxy;
+
+/**
+ * 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 @@
* }
* </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,31 +18,22 @@
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 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.proxy.cglib.CglibProxyFactory;
+import org.apache.wicket.proxy.jdk.JdkProxyFactory;
import org.apache.wicket.util.io.IClusterable;
-import org.apache.wicket.util.string.Strings;
+
+import net.sf.cglib.core.DefaultNamingPolicy;
+import net.sf.cglib.core.Predicate;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import net.sf.cglib.proxy.NoOp;
/**
* A factory class that creates lazy init proxies given a type and a {@link IProxyTargetLocator}
@@ -53,7 +44,7 @@
* 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 @@
/**
* 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 @@
}
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);
- }
-
+ return new JdkProxyFactory().createProxy(type, locator);
}
- 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();
+ else {
+ return proxyFactory.createProxy(type, locator);
}
}
-
- 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;
- }
-
- /**
+
+ /**
* 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 @@
*/
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 @@
* @author Igor Vaynberg (ivaynberg)
*
*/
- static class ProxyReplacement implements IClusterable
+ public static class ProxyReplacement implements IClusterable
{
private static final long serialVersionUID = 1L;
@@ -286,266 +234,6 @@
}
/**
- * Method interceptor for proxies representing concrete object not backed by an interface. These
- * proxies are represented by cglib proxies.
- *
- * @author Igor Vaynberg (ivaynberg)
- *
- */
- public abstract static class AbstractCGLibInterceptor
- implements
- MethodInterceptor,
- 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 AbstractCGLibInterceptor(final Class<?> type, final IProxyTargetLocator locator)
- {
- super();
- typeName = type.getName();
- this.locator = locator;
- }
-
- /**
- * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object,
- * java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
- */
- @Override
- public Object intercept(final Object object, final Method method, final Object[] args,
- final MethodProxy proxy) 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 (isWriteReplaceMethod(method))
- {
- return writeReplace();
- }
- else if (method.getDeclaringClass().equals(ILazyInitProxy.class))
- {
- return getObjectLocator();
- }
-
- if (target == null)
- {
- target = locator.locateProxyTarget();
- }
- return proxy.invoke(target, args);
- }
-
- /**
- * @see org.apache.wicket.proxy.ILazyInitProxy#getObjectLocator()
- */
- @Override
- public IProxyTargetLocator getObjectLocator()
- {
- return locator;
- }
- }
-
- /**
- * Method interceptor for proxies representing concrete object not backed by an interface. These
- * proxies are representing by cglib proxies.
- *
- * @author Igor Vaynberg (ivaynberg)
- */
- protected 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
- {
- return new ProxyReplacement(typeName, locator);
- }
- }
-
- /**
- * Serializable implementation of the NoOp callback.
- */
- public static class SerializableNoOpCallback implements NoOp, Serializable
- {
- 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);
- }
- }
-
- /**
* Checks if the method is derived from Object.equals()
*
* @param method
@@ -611,7 +299,126 @@
(method.getParameterTypes().length == 0) && method.getName().equals("writeReplace");
}
- public static final class WicketNamingPolicy extends DefaultNamingPolicy
+ /**
+ * Method interceptor for proxies representing concrete object not backed by an interface. These
+ * proxies are represented by cglib proxies.
+ *
+ * @author Igor Vaynberg (ivaynberg)
+ *
+ */
+ public abstract static class AbstractCGLibInterceptor
+ implements
+ MethodInterceptor,
+ 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 AbstractCGLibInterceptor(final Class<?> type, final IProxyTargetLocator locator)
+ {
+ super();
+ typeName = type.getName();
+ this.locator = locator;
+ }
+
+ /**
+ * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object,
+ * java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
+ */
+ @Override
+ public Object intercept(final Object object, final Method method, final Object[] args,
+ final MethodProxy proxy) 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 (LazyInitProxyFactory.isWriteReplaceMethod(method))
+ {
+ return writeReplace();
+ }
+ else if (method.getDeclaringClass().equals(ILazyInitProxy.class))
+ {
+ return getObjectLocator();
+ }
+
+ if (target == null)
+ {
+ target = locator.locateProxyTarget();
+ }
+ return proxy.invoke(target, args);
+ }
+
+ /**
+ * @see org.apache.wicket.proxy.ILazyInitProxy#getObjectLocator()
+ */
+ @Override
+ public IProxyTargetLocator getObjectLocator()
+ {
+ return locator;
+ }
+ }
+
+ /**
+ * Method interceptor for proxies representing concrete object not backed by an interface. These
+ * proxies are representing by cglib proxies.
+ *
+ * @author Igor Vaynberg (ivaynberg)
+ */
+ public static class CGLibInterceptor extends AbstractCGLibInterceptor
+ {
+ public CGLibInterceptor(Class<?> type, IProxyTargetLocator locator)
+ {
+ super(type, locator);
+ }
+
+ @Override
+ public Object writeReplace() throws ObjectStreamException
+ {
+ return new ProxyReplacement(typeName, locator);
+ }
+ }
+
+ /**
+ * Serializable implementation of the NoOp callback.
+ */
+ public static class SerializableNoOpCallback implements NoOp, Serializable
+ {
+ private static final long serialVersionUID = 1L;
+
+ public static final NoOp INSTANCE = new SerializableNoOpCallback();
+ }
+
+ public static class WicketNamingPolicy extends DefaultNamingPolicy
{
public static final WicketNamingPolicy INSTANCE = new WicketNamingPolicy();
@@ -632,25 +439,4 @@
}
}
-
- 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<String></em> and <em>ArrayList<Integer></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/bytebuddy/ObjenesisByteBuddyInterceptor.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
new file mode 100644
index 0000000..bac1b64
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.proxy.bytebuddy;
+
+import java.io.ObjectStreamException;
+
+import org.apache.wicket.proxy.IProxyTargetLocator;
+
+/**
+ * Method interceptor for proxies representing concrete object not backed by an interface.
+ * These proxies are representing by ByteBuddy proxies.
+ */
+public class ObjenesisByteBuddyInterceptor extends ByteBuddyProxyFactory.ByteBuddyInterceptor
+{
+ public ObjenesisByteBuddyInterceptor(Class<?> type, IProxyTargetLocator locator) {
+ super(type, locator);
+ }
+
+ @Override
+ public Object writeReplace() throws ObjectStreamException
+ {
+ return new ObjenesisProxyReplacement(typeName, locator);
+ }
+}
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java
new file mode 100644
index 0000000..45cf623
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.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.wicket.proxy.bytebuddy;
+
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.objenesis.ObjenesisStd;
+
+public class ObjenesisProxyFactory
+{
+ private static final ObjenesisStd OBJENESIS = new ObjenesisStd(false);
+
+ public static Object createProxy(final Class<?> type, final IProxyTargetLocator 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/bytebuddy/ObjenesisProxyReplacement.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
new file mode 100644
index 0000000..4430182
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
@@ -0,0 +1,54 @@
+/*
+ * 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 org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.core.util.lang.WicketObjects;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.util.io.IClusterable;
+
+/**
+ * Object that replaces the proxy when it is serialized. Upon deserialization this object will
+ * create a new proxy with the same locator.
+ */
+class ObjenesisProxyReplacement implements IClusterable
+{
+ private static final long serialVersionUID = 1L;
+
+ private final IProxyTargetLocator locator;
+
+ private final String type;
+
+ ObjenesisProxyReplacement(final String type, final IProxyTargetLocator locator) {
+ this.type = type;
+ this.locator = locator;
+ }
+
+ protected Object readResolve() throws ObjectStreamException
+ {
+ Class<?> clazz = WicketObjects.resolveClass(type);
+ if (clazz == null) {
+ ClassNotFoundException cause = new ClassNotFoundException(
+ "Could not resolve type [" + type +
+ "] with the currently configured org.apache.wicket.application.IClassResolver");
+ throw new WicketRuntimeException(cause);
+ }
+ 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 @@
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 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 @@
"] 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 @@
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 @@
@Bean
public List<String> stringsList()
{
- return Arrays.asList("foo", "bar", "baz");
+ return List.of("foo", "bar", "baz");
}
@Bean