Using Weld 4.0.1.SP1 instead of 4.0.0.Final
- reimplemented ProxyServicesImpl, because Weld changed behavior and added
methods to the interface and previous implementation caused failure of
cdi_all tests
diff --git a/appserver/pom.xml b/appserver/pom.xml
index 639e2f8..1e49f3a 100644
--- a/appserver/pom.xml
+++ b/appserver/pom.xml
@@ -158,7 +158,7 @@
<!-- CDI -->
<cdi-api.version>3.0.0</cdi-api.version>
- <weld.version>4.0.0.Final</weld.version>
+ <weld.version>4.0.1.SP1</weld.version>
<jboss.classfilewriter.version>1.2.4.Final</jboss.classfilewriter.version>
<!-- Admin console components -->
diff --git a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/services/ProxyServicesImpl.java b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/services/ProxyServicesImpl.java
index 859eab1..4479088 100644
--- a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/services/ProxyServicesImpl.java
+++ b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/services/ProxyServicesImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2018-2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,124 +16,203 @@
package org.glassfish.weld.services;
-import static java.lang.System.getSecurityManager;
-import static java.security.AccessController.doPrivileged;
-
+import java.lang.reflect.Method;
+import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.internal.api.ClassLoaderHierarchy;
import org.jboss.weld.serialization.spi.ProxyServices;
/**
- * An implementation of the <code>ProxyServices</code> Service.
- *
- * This implementation uses the thread context classloader (the application classloader) as the classloader for loading
- * the bean proxies. The classloader that loaded the Bean must be used to load and define the bean proxy to handle Beans
- * with package-private constructor as discussed in WELD-737.
- *
- * Weld proxies today have references to some internal weld implementation classes such as javassist and
- * org.jboss.weld.proxy.* packages. These classes are temporarily re-exported through the weld-integration-fragment
- * bundle so that when the bean proxies when loaded using the application classloader will have visibility to these
- * internal implementation classes.
- *
- * As a fix for WELD-737, Weld may use the Bean's classloader rather than asking the ProxyServices service
- * implementation. Weld also plans to remove the dependencies of the bean proxy on internal implementation classes. When
- * that happens we can remove the weld-integration-fragment workaround and the ProxyServices implementation
+ * An implementation of the {@link ProxyServices}.
+ * <p>
+ * This implementation respects the classloader hierarchy used to load original beans.
+ * If it is not an application classloader, uses the current thread classloader.
+ * If it wasn't possible to detect any usable classloader, throws a {@link WeldProxyException}
+ * <p>
+ * Context: Weld generates proxies for beans from an application and for certain API artifacts
+ * such as <code>UserTransaction</code>.
*
* @author Sivakumar Thyagarajan
+ * @author David Matějček
*/
public class ProxyServicesImpl implements ProxyServices {
- ClassLoaderHierarchy classLoaderHierarchy;
+ private static Method defineClassMethod;
+ private static Method defineClassMethodSM;
+ private static final AtomicBoolean CL_METHODS_INITIALIZATION_FINISHED = new AtomicBoolean(false);
- public ProxyServicesImpl(ServiceLocator services) {
+ private final ClassLoaderHierarchy classLoaderHierarchy;
+
+
+ /**
+ * @param services immediately used to find a {@link ClassLoaderHierarchy} service
+ */
+ public ProxyServicesImpl(final ServiceLocator services) {
classLoaderHierarchy = services.getService(ClassLoaderHierarchy.class);
}
+
+ @Deprecated
+ @Override
+ public boolean supportsClassDefining() {
+ // true is mandatory since Weld 4.0.1.SP1, because default method impl returns false
+ // and cdi_all tests then fail
+ return true;
+ }
+
+
+ @Deprecated
@Override
public ClassLoader getClassLoader(final Class<?> proxiedBeanType) {
- if (getSecurityManager() == null) {
+ if (System.getSecurityManager() == null) {
return getClassLoaderforBean(proxiedBeanType);
}
- return doPrivileged(new PrivilegedAction<ClassLoader>() {
- @Override
- public ClassLoader run() {
- return getClassLoaderforBean(proxiedBeanType);
- }
- });
+ final PrivilegedAction<ClassLoader> action = () -> getClassLoaderforBean(proxiedBeanType);
+ return AccessController.doPrivileged(action);
}
- /**
- * Gets the ClassLoader associated with the Bean. Weld generates Proxies for Beans from an application/BDA and for
- * certain API artifacts such as <code>UserTransaction</code>.
- *
- * @param proxiedBeanType
- * @return
- */
- private ClassLoader getClassLoaderforBean(Class<?> proxiedBeanType) {
- // Get the ClassLoader that loaded the Bean. For Beans in an application,
- // this would be the application/module classloader. For other API
- // Bean classes, such as UserTransaction, this would be a non-application
- // classloader
- ClassLoader proxyClassLoader = proxiedBeanType.getClassLoader();
- //Check if this is an application classloader
- boolean isAppCL = isApplicationClassLoader(proxyClassLoader);
- if (!isAppCL) {
- proxyClassLoader = _getClassLoader();
- //fall back to the old behaviour of using TCL to get the application
- //or module classloader. We return this classloader for non-application
- //Beans, as Weld Proxies requires other Weld support classes (such as
- //JBoss Reflection API) that is exported through the weld-osgi-bundle.
- }
-
- return proxyClassLoader;
- }
-
- /**
- * Check if the ClassLoader of the Bean type being proxied is a GlassFish application ClassLoader. The current logic
- * checks if the common classloader appears as a parent in the classloader hierarchy of the Bean's classloader.
- */
- private boolean isApplicationClassLoader(ClassLoader prxCL) {
- boolean isAppCL = false;
- while (prxCL != null) {
- if (prxCL.equals(classLoaderHierarchy.getCommonClassLoader())) {
- isAppCL = true;
- break;
- }
- prxCL = prxCL.getParent();
- }
- return isAppCL;
- }
-
- private ClassLoader _getClassLoader() {
- return Thread.currentThread().getContextClassLoader();
- }
-
+ @Deprecated
@Override
public Class<?> loadBeanClass(final String className) {
try {
- if (getSecurityManager() == null) {
- return Class.forName(className, true, _getClassLoader());
+ if (System.getSecurityManager() == null) {
+ return loadClassByThreadCL(className);
}
-
- return (Class<?>) doPrivileged(new PrivilegedExceptionAction<>() {
- @Override
- public Object run() throws Exception {
- return Class.forName(className, true, _getClassLoader());
- }
- });
- } catch (Exception ex) {
- ex.printStackTrace();
- throw new RuntimeException(ex);
+ final PrivilegedExceptionAction<Class<?>> action = () -> loadClassByThreadCL(className);
+ return AccessController.doPrivileged(action);
+ } catch (final Exception ex) {
+ throw new WeldProxyException("Failed to load the bean class: " + className, ex);
}
}
+
+ @Override
+ public Class<?> defineClass(final Class<?> originalClass, final String className, final byte[] classBytes,
+ final int off, final int len) throws ClassFormatError {
+ return defineClass(originalClass, className, classBytes, off, len, null);
+ }
+
+
+ @Override
+ public Class<?> defineClass(final Class<?> originalClass, final String className, final byte[] classBytes,
+ final int off, final int len, final ProtectionDomain protectionDomain) throws ClassFormatError {
+ checkClassDefinitionFeature();
+ final ClassLoader loader = getClassLoaderforBean(originalClass);
+ if (protectionDomain == null) {
+ return defineClass(loader, className, classBytes, off, len);
+ }
+ return defineClass(loader, className, classBytes, off, len, protectionDomain);
+ }
+
+
+ @Override
+ public Class<?> loadClass(final Class<?> originalClass, final String classBinaryName)
+ throws ClassNotFoundException {
+ return getClassLoaderforBean(originalClass).loadClass(classBinaryName);
+ }
+
+
@Override
public void cleanup() {
// nothing to cleanup in this implementation.
}
+
+ /**
+ * Initialization of access to protected methods of the {@link ClassLoader} class.
+ */
+ private static void checkClassDefinitionFeature() {
+ if (CL_METHODS_INITIALIZATION_FINISHED.compareAndSet(false, true)) {
+ try {
+ final PrivilegedExceptionAction<Void> action = () -> {
+ final Class<?> cl = Class.forName("java.lang.ClassLoader");
+ final String name = "defineClass";
+ defineClassMethod = cl.getDeclaredMethod(name, String.class, byte[].class, int.class, int.class);
+ defineClassMethod.setAccessible(true);
+ defineClassMethodSM = cl.getDeclaredMethod(
+ name, String.class, byte[].class, int.class, int.class, ProtectionDomain.class);
+ defineClassMethodSM.setAccessible(true);
+ return null;
+ };
+ AccessController.doPrivileged(action);
+ } catch (final Exception e) {
+ throw new WeldProxyException("Could not initialize access to ClassLoader.defineClass method.", e);
+ }
+ }
+ }
+
+
+ /**
+ * @param originalClass
+ * @return ClassLoader probably usable with the bean.
+ */
+ private ClassLoader getClassLoaderforBean(final Class<?> originalClass) {
+ // Get the ClassLoader that loaded the Bean. For Beans in an application,
+ // this would be the application/module classloader. For other API
+ // Bean classes, such as UserTransaction, this would be a non-application
+ // classloader
+ final ClassLoader originalClassLoader = originalClass.getClassLoader();
+ if (isApplicationClassLoader(originalClassLoader)) {
+ return originalClassLoader;
+ }
+ // fall back to the old behaviour of using thread class loader to get the application
+ // or module classloader. We return this classloader for non-application
+ // Beans, as Weld Proxies requires other Weld support classes (such as
+ // JBoss Reflection API) that is exported through the weld-osgi-bundle.
+ final ClassLoader threadCL = Thread.currentThread().getContextClassLoader();
+ if (threadCL != null) {
+ return threadCL;
+ }
+ throw new WeldProxyException("Could not determine classloader for " + originalClass);
+ }
+
+
+ /**
+ * Check if the ClassLoader of the Bean type being proxied is a GlassFish application
+ * ClassLoader. The current logic checks if the common classloader appears as a parent in
+ * the classloader hierarchy of the Bean's classloader.
+ */
+ private boolean isApplicationClassLoader(ClassLoader classLoader) {
+ while (classLoader != null) {
+ if (classLoader.equals(classLoaderHierarchy.getCommonClassLoader())) {
+ return true;
+ }
+ classLoader = classLoader.getParent();
+ }
+ return false;
+ }
+
+
+ private Class<?> loadClassByThreadCL(final String className) throws ClassNotFoundException {
+ return Class.forName(className, true, Thread.currentThread().getContextClassLoader());
+ }
+
+
+ private Class<?> defineClass(
+ final ClassLoader loader, final String className,
+ final byte[] b, final int off, final int len,
+ final ProtectionDomain protectionDomain) {
+ try {
+ return (Class<?>) defineClassMethodSM.invoke(loader, className, b, 0, len, protectionDomain);
+ } catch (final Exception e) {
+ throw new WeldProxyException("Could not define class " + className, e);
+ }
+ }
+
+
+ private Class<?> defineClass(
+ final ClassLoader loader, final String className,
+ final byte[] b, final int off, final int len) {
+ try {
+ return (Class<?>) defineClassMethod.invoke(loader, className, b, 0, len);
+ } catch (final Exception e) {
+ throw new WeldProxyException("Could not define class " + className, e);
+ }
+ }
}
diff --git a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/services/WeldProxyException.java b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/services/WeldProxyException.java
new file mode 100644
index 0000000..d3beaba
--- /dev/null
+++ b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/services/WeldProxyException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.weld.services;
+
+
+/**
+ * Runtime exception meaning that the operation failed to finish the desired operation.
+ *
+ * @author David Matějček
+ */
+public class WeldProxyException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public WeldProxyException(final String message, final Exception cause) {
+ super(message, cause);
+ }
+
+
+ public WeldProxyException(final String message) {
+ super(message);
+ }
+}