/* * Copyright 2012 Harald Wellmann. * * Licensed 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.ops4j.pax.cdi.weld.impl.util; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import org.jboss.weld.interceptor.proxy.LifecycleMixin; import org.jboss.weld.serialization.spi.ProxyServices; import org.jboss.weld.util.Types; import org.ops4j.pax.cdi.spi.util.Exceptions; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.Extension; /** * Implements {@link ProxyServices} for Weld in OSGi runtime. * * @see org.ops4j.pax.cdi.weld.impl.ProxyWeavingHook * * @author Harald Wellmann * */ public class OsgiProxyService implements ProxyServices { private final BeanManager manager; private final DelegatingBundle delegate; private final ClassLoader loader; public OsgiProxyService(BeanManager manager, ClassLoader parent) { this.manager = manager; // Initiate the delegate classloader to contain the Weld mixin classes // TODO: use version from xbean-bundleutils when it provides a way to avoid // adding the same bundle multiple times this.delegate = new DelegatingBundle(FrameworkUtil.getBundle(LifecycleMixin.class)); this.loader = new BundleClassLoader(delegate, parent); } @Override public ClassLoader getClassLoader(Class<?> proxiedBeanType) { if (!Modifier.isPublic(proxiedBeanType.getModifiers()) && !Modifier.isProtected(proxiedBeanType.getModifiers())) { // For package-private bean type, we must used the same defining classloader // as that of the bean type otherwise IllegalAccessError is thrown and there // seems to be no obvious way to do proper adaptation of the classloader // in case the bean type closure spans multiple bundles. // For Weld mixin classes, we rely on the ProxyWeavingHook that adds dynamic // imports for the corresponding packages. return proxiedBeanType.getClassLoader(); } else if (Extension.class.isAssignableFrom(proxiedBeanType)) { // It happens that extensions often declare package-private or public methods // whose return type is package-private hence forcing to define the corresponding // proxy classes in the same defining classloaders as those of the extension types // otherwise IllegalAccessError is thrown. return proxiedBeanType.getClassLoader(); } // TODO: it may be necessary to iterate over the whole list of proxied methods from // the type closure and check for package-private or public methods whose parameters // or return type are package-private. else { // For non package-private bean type, we retrieve its type closure that may // span multiple bundles and add then to list of the delegating bundles. // We use the bean managed to leverage its annotated types cache. for (Class<?> type : Types.getRawTypes(manager.createAnnotatedType(proxiedBeanType).getTypeClosure())) { Bundle bundle = FrameworkUtil.getBundle(type); if (bundle != null) { delegate.addBundle(bundle); } } return loader; } } @Override public Class<?> loadBeanClass(final String className) { try { return AccessController.doPrivileged(new LoadClass(className)); } catch (PrivilegedActionException pae) { throw Exceptions.unchecked(pae); } } @Override public void cleanup() { // empty } private class LoadClass implements PrivilegedExceptionAction<Class<?>> { private final String className; private LoadClass(String className) { this.className = className; } @Override public Class<?> run() throws ClassNotFoundException { return Class.forName(className, true, loader); } } }