diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java index 2e55804b10..5598e8ea05 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java @@ -42,6 +42,10 @@ public class BeanManagerListenerFactory implements ListenerFactory { private final BeanManager beanManager; private final Map listeners = new ConcurrentHashMap(); + public static BeanManagerListenerFactory fromBeanManagerReference(Object beanManagerReference) { + return new BeanManagerListenerFactory( ( BeanManager ) beanManagerReference ); + } + public BeanManagerListenerFactory(BeanManager beanManager) { this.beanManager = beanManager; } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java index f2ca0f4fac..0c05b5b9fb 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java @@ -23,7 +23,8 @@ */ package org.hibernate.jpa.event.spi; -import javax.enterprise.inject.spi.BeanManager; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Iterator; import java.util.Map; @@ -187,10 +188,10 @@ public class JpaIntegrator implements Integrator { // handle JPA "entity listener classes"... this.callbackRegistry = new CallbackRegistryImpl(); - final BeanManager beanManager = (BeanManager) configuration.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER ); - this.jpaListenerFactory = beanManager == null + final Object beanManagerRef = configuration.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER ); + this.jpaListenerFactory = beanManagerRef == null ? new StandardListenerFactory() - : new BeanManagerListenerFactory( beanManager ); + : buildBeanManagerListenerFactory( beanManagerRef ); this.callbackProcessor = new LegacyCallbackProcessor( jpaListenerFactory, configuration.getReflectionManager() ); Iterator classes = configuration.getClassMappings(); @@ -213,6 +214,34 @@ public class JpaIntegrator implements Integrator { } } + private ListenerFactory buildBeanManagerListenerFactory(Object beanManagerRef) { + try { + // specifically using our classloader here... + final Class beanManagerListenerFactoryClass = getClass().getClassLoader() + .loadClass( "org.hibernate.jpa.event.internal.jpa.BeanManagerListenerFactory" ); + final Method beanManagerListenerFactoryBuilderMethod = beanManagerListenerFactoryClass.getMethod( + "fromBeanManagerReference", + Object.class + ); + + try { + return (ListenerFactory) beanManagerListenerFactoryBuilderMethod.invoke( null, beanManagerRef ); + } + catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + catch (ReflectiveOperationException e) { + throw new HibernateException( "Could not access BeanManagerListenerFactory class to handle CDI extensions", e ); + } + catch (RuntimeException e) { + throw e; + } + catch (Throwable e) { + throw new HibernateException( "Problem calling BeanManagerListenerFactory class to handle CDI extensions", e ); + } + } + @Override public void integrate( MetadataImplementor metadata, @@ -288,10 +317,10 @@ public class JpaIntegrator implements Integrator { // handle JPA "entity listener classes"... this.callbackRegistry = new CallbackRegistryImpl(); - final BeanManager beanManager = (BeanManager) sessionFactory.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER ); - this.jpaListenerFactory = beanManager == null + final Object beanManagerRef = sessionFactory.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER ); + this.jpaListenerFactory = beanManagerRef == null ? new StandardListenerFactory() - : new BeanManagerListenerFactory( beanManager ); + : buildBeanManagerListenerFactory( beanManagerRef ); this.callbackProcessor = new CallbackProcessorImpl( jpaListenerFactory, metadata, serviceRegistry ); for ( EntityBinding binding : metadata.getEntityBindings() ) { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/NoCdiAvailableTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/NoCdiAvailableTest.java new file mode 100644 index 0000000000..48b4cb9ddb --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/NoCdiAvailableTest.java @@ -0,0 +1,138 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.cdi; + +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; + +import org.hibernate.bytecode.spi.ByteCodeHelper; + +import org.junit.Rule; +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.junit4.ClassLoadingIsolater; + +import static org.junit.Assert.fail; + +/** + * Test JPA bootstrapping when CDI is not available for classloading. + * + * @author Steve Ebersole + */ +public class NoCdiAvailableTest extends BaseUnitTestCase { + public static final String[] EXCLUDED_PACKAGES = new String[] { + "javax.enterprise.inject.", + "javax.enterprise.context." + }; + + private static class CdiClassLoadException extends RuntimeException { + private CdiClassLoadException(String message) { + super( message ); + } + } + + @Rule public ClassLoadingIsolater isolater = new ClassLoadingIsolater( + new ClassLoadingIsolater.IsolatedClassLoaderProvider() { + + @Override + public ClassLoader buildIsolatedClassLoader() { + return new ClassLoader(NoCdiAvailableTest.class.getClassLoader()) { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + for ( String excludedPackage : EXCLUDED_PACKAGES ) { + if ( name.startsWith( excludedPackage ) ) { + throw new CdiClassLoadException( "CDI classes [" + name + "] excluded from load" ); + } + } + + Class c = findLoadedClass( name ); + if ( c != null ) { + return c; + } + + final String resourceName = name.replace( '.', '/' ) + ".class"; + final URL resource = getResource( resourceName ); + if ( resource == null ) { + throw new ClassNotFoundException( name + " not found" ); + } + if ( !"file".equals( resource.getProtocol() ) ) { + return getParent().loadClass( name ); + } + + InputStream is = this.getResourceAsStream( name.replace( '.', '/' ) + ".class" ); + if ( is == null ) { + throw new ClassNotFoundException( name + " not found" ); + } + + try { + byte[] bytecode = ByteCodeHelper.readByteCode( is ); + return defineClass( name, bytecode, 0, bytecode.length ); + } + catch( Throwable t ) { + throw new ClassNotFoundException( "Error reading class file for " + name, t ); + } + } + }; + } + + @Override + public void releaseIsolatedClassLoader(ClassLoader isolatedClassLoader) { + // nothing to do + } + } + ); + + @Test + public void testJpaBootstrapWithoutCdiAvailable() throws Exception { + Class delegateClass = Thread.currentThread().getContextClassLoader().loadClass( + "org.hibernate.jpa.test.cdi.NoCdiAvailableTestDelegate" + ); + Method mainMethod = delegateClass.getMethod( "passingNoBeanManager" ); + mainMethod.invoke( null ); + } + + @Test + public void testJpaBootstrapWithoutCdiAvailablePassingCdi() throws Throwable { + Class delegateClass = Thread.currentThread().getContextClassLoader().loadClass( + "org.hibernate.jpa.test.cdi.NoCdiAvailableTestDelegate" + ); + Method mainMethod = delegateClass.getMethod( "passingBeanManager" ); + try { + mainMethod.invoke( null ); + fail( "Expecting failure from missing CDI classes" ); + } + catch (InvocationTargetException e) { + try { + throw e.getTargetException(); + } + catch (CdiClassLoadException expected) { + } +// catch (ClassCastException expectedAlt) { +// } + } + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/NoCdiAvailableTestDelegate.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/NoCdiAvailableTestDelegate.java new file mode 100644 index 0000000000..36fc59a044 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/NoCdiAvailableTestDelegate.java @@ -0,0 +1,49 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.cdi; + +import java.util.Collections; + +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.jpa.test.PersistenceUnitInfoAdapter; + +/** + * @author Steve Ebersole + */ +public class NoCdiAvailableTestDelegate { + public static void passingNoBeanManager() { + new HibernatePersistenceProvider().createContainerEntityManagerFactory( + new PersistenceUnitInfoAdapter(), + Collections.emptyMap() + ); + } + + public static void passingBeanManager() { + new HibernatePersistenceProvider().createContainerEntityManagerFactory( + new PersistenceUnitInfoAdapter(), + Collections.singletonMap( AvailableSettings.CDI_BEAN_MANAGER, new Object() ) + ); + } +}