blob: 1ccf137adc44ffd0a286523be43a6140cafac261 [file] [log] [blame]
/*
* Copyright (c) 2010, 2018-2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022 Contributors to the Eclipse Foundation
*
* 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;
import com.sun.ejb.ClassGenerator;
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 {@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 {
private static final AtomicBoolean CL_METHODS_INITIALIZATION_FINISHED = new AtomicBoolean(false);
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 (System.getSecurityManager() == null) {
return getClassLoaderforBean(proxiedBeanType);
}
final PrivilegedAction<ClassLoader> action = () -> getClassLoaderforBean(proxiedBeanType);
return AccessController.doPrivileged(action);
}
@Deprecated
@Override
public Class<?> loadBeanClass(final String className) {
try {
if (System.getSecurityManager() == null) {
return loadClassByThreadCL(className);
}
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 {
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.
}
/**
* @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 ClassGenerator.defineClass(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 ClassGenerator.defineClass(loader, className, b, 0, len);
} catch (final Exception e) {
throw new WeldProxyException("Could not define class " + className, e);
}
}
}