/** * * 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.openejb.core.cmp.jpa; import junit.framework.TestCase; import org.apache.geronimo.transaction.manager.GeronimoTransactionManager; import org.apache.openejb.core.TempClassLoader; import org.apache.openejb.javaagent.Agent; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.persistence.PersistenceClassLoaderHandler; import org.apache.openejb.persistence.PersistenceUnitInfoImpl; import org.apache.openejb.resource.jdbc.dbcp.BasicDataSource; import org.apache.openejb.resource.jdbc.dbcp.BasicManagedDataSource; import javax.persistence.EntityManagerFactory; import javax.persistence.spi.PersistenceProvider; import javax.persistence.spi.PersistenceUnitTransactionType; import javax.sql.DataSource; import javax.transaction.TransactionManager; import javax.transaction.TransactionSynchronizationRegistry; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLClassLoader; import java.security.ProtectionDomain; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; import java.util.Properties; public class UnenhancedTest extends TestCase { private static final String PERSISTENCE_PROVIDER = "org.apache.openjpa.persistence.PersistenceProviderImpl"; private GeronimoTransactionManager transactionManager; private DataSource jtaDs; private DataSource nonJtaDs; private EntityManagerFactory entityManagerFactory; private boolean enhance; public void setUp() throws Exception { super.setUp(); // setup tx mgr transactionManager = new GeronimoTransactionManager(); SystemInstance.get().setComponent(TransactionSynchronizationRegistry.class, transactionManager); // Put tx mgr into SystemInstance so OpenJPA can find it SystemInstance.get().setComponent(TransactionManager.class, transactionManager); // init databases jtaDs = createJtaDataSource(transactionManager); nonJtaDs = createNonJtaDataSource(); } public void tearDown() throws Exception { if (entityManagerFactory != null && entityManagerFactory.isOpen()) { entityManagerFactory.close(); } if (nonJtaDs != null) { final Connection connection = nonJtaDs.getConnection(); final Statement statement = connection.createStatement(); statement.execute("SHUTDOWN"); close(statement); close(connection); } nonJtaDs = null; jtaDs = null; // diable any enhancers we added enhance = false; super.tearDown(); } public void testEnhancedComplexIdJta() throws Exception { runTest("complexId", PersistenceUnitTransactionType.JTA, true); } public void testEnhancedComplexIdResourceLocal() throws Exception { runTest("complexId", PersistenceUnitTransactionType.RESOURCE_LOCAL, true); } public void testUnenhancedComplexIdJta() throws Exception { runTest("complexId", PersistenceUnitTransactionType.JTA, false); } public void testUnenhancedComplexIdResourceLocal() throws Exception { runTest("complexId", PersistenceUnitTransactionType.RESOURCE_LOCAL, false); } public void testEnhancedComplexIdSubclassJta() throws Exception { runTest("complexIdSubclass", PersistenceUnitTransactionType.JTA, true); } public void testEnhancedComplexIdSubclassResourceLocal() throws Exception { runTest("complexIdSubclass", PersistenceUnitTransactionType.RESOURCE_LOCAL, true); } // todo OpenJPA public void XtestUnenhancedComplexIdSubclassJta() throws Exception { runTest("complexIdSubclass", PersistenceUnitTransactionType.JTA, false); } // todo OpenJPA public void XtestUnenhancedComplexIdSubclassResourceLocal() throws Exception { runTest("complexIdSubclass", PersistenceUnitTransactionType.RESOURCE_LOCAL, false); } public void testEnhancedGeneratedIdJta() throws Exception { runTest("generatedId", PersistenceUnitTransactionType.JTA, true); } public void testEnhancedGeneratedIdResourceLocal() throws Exception { runTest("generatedId", PersistenceUnitTransactionType.RESOURCE_LOCAL, true); } // todo OpenJPA public void XtestUnenhancedGeneratedIdJta() throws Exception { runTest("generatedId", PersistenceUnitTransactionType.JTA, false); } // todo OpenJPA public void XtestUnenhancedGeneratedIdResourceLocal() throws Exception { runTest("generatedId", PersistenceUnitTransactionType.RESOURCE_LOCAL, false); } public void testEnhancedGeneratedIdSubclassJta() throws Exception { runTest("generatedIdSubclass", PersistenceUnitTransactionType.JTA, true); } public void testEnhancedGeneratedIdSubclassResourceLocal() throws Exception { runTest("generatedIdSubclass", PersistenceUnitTransactionType.RESOURCE_LOCAL, true); } // todo OpenJPA public void XtestUnenhancedGeneratedIdSubclassJta() throws Exception { runTest("generatedIdSubclass", PersistenceUnitTransactionType.JTA, false); } // todo OpenJPA public void XtestUnenhancedGeneratedIdSubclassResourceLocal() throws Exception { runTest("generatedIdSubclass", PersistenceUnitTransactionType.RESOURCE_LOCAL, false); } public void testEnhancedCollectionJta() throws Exception { runTest("collection", PersistenceUnitTransactionType.JTA, true); } public void testEnhancedCollectionResourceLocal() throws Exception { runTest("collection", PersistenceUnitTransactionType.RESOURCE_LOCAL, true); } // todo OpenJPA public void XtestUnenhancedCollectionJta() throws Exception { runTest("collection", PersistenceUnitTransactionType.JTA, false); } // todo OpenJPA public void XtestUnenhancedCollectionResourceLocal() throws Exception { runTest("collection", PersistenceUnitTransactionType.RESOURCE_LOCAL, false); } private void runTest(final String methodName, final PersistenceUnitTransactionType transactionType, final boolean enhance) throws Exception { this.enhance = enhance; final ClassLoader loader = new FilteredChildFirstClassLoader(getClass().getClassLoader(), "org.apache.openejb.core.cmp.jpa"); final PersistenceClassLoaderHandler persistenceClassLoaderHandler = new PersistenceClassLoaderHandler() { public void addTransformer(final String unitId, final ClassLoader classLoader, final ClassFileTransformer classFileTransformer) { final Instrumentation instrumentation = Agent.getInstrumentation(); if (instrumentation != null) { instrumentation.addTransformer(new ControllableTransformer(classFileTransformer)); } } public void destroy(final String unitId) { } public ClassLoader getNewTempClassLoader(final ClassLoader classLoader) { return new TempClassLoader(classLoader); } }; final PersistenceUnitInfoImpl unitInfo = new PersistenceUnitInfoImpl(persistenceClassLoaderHandler); unitInfo.setPersistenceUnitName("CMP"); unitInfo.setPersistenceProviderClassName(PERSISTENCE_PROVIDER); unitInfo.setClassLoader(loader); unitInfo.setExcludeUnlistedClasses(false); unitInfo.setJtaDataSource(jtaDs); unitInfo.setNonJtaDataSource(nonJtaDs); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.ComplexSuperclass"); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.ComplexSubclass"); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.ComplexStandalone"); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.GeneratedStandalone"); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.GeneratedSuperclass"); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.GeneratedSubclass"); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.OneStandalone"); unitInfo.addManagedClassName("org.apache.openejb.core.cmp.jpa.ManyStandalone"); // Handle Properties final Properties properties = new Properties(); properties.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(SchemaAction='add,deleteTableContents',ForeignKeys=true)"); properties.setProperty("openjpa.RuntimeUnenhancedClasses", "supported"); properties.setProperty("openjpa.Log", "DefaultLevel=INFO"); unitInfo.setProperties(properties); unitInfo.setTransactionType(transactionType); unitInfo.getManagedClassNames().add("org.apache.openejb.core.cmp.jpa.Employee"); final PersistenceProvider persistenceProvider = (PersistenceProvider) getClass().getClassLoader().loadClass(PERSISTENCE_PROVIDER).newInstance(); entityManagerFactory = persistenceProvider.createContainerEntityManagerFactory(unitInfo, new HashMap()); // create the test object (via reflection) final Object testObject = loader.loadClass("org.apache.openejb.core.cmp.jpa.UnenhancedUnits").newInstance(); set(testObject, "TransactionManager", TransactionManager.class, transactionManager); set(testObject, "EntityManagerFactory", EntityManagerFactory.class, entityManagerFactory); // invoke the test (via reflection) Thread.currentThread().setContextClassLoader(loader); invoke(testObject, "setUp"); try { invoke(testObject, methodName); } finally { invoke(testObject, "tearDown"); } } private DataSource createJtaDataSource(final TransactionManager transactionManager) throws Exception { final BasicManagedDataSource ds = new BasicManagedDataSource(getClass().getName() + ".createJtaDs"); ds.setTransactionManager(transactionManager); ds.setDriverClassName("org.hsqldb.jdbcDriver"); ds.setUrl("jdbc:hsqldb:mem:JpaTest"); ds.setUsername("sa"); ds.setPassword(""); ds.setMaxTotal(100); ds.setMaxWait(10000); ds.setTestOnBorrow(true); return ds; } private DataSource createNonJtaDataSource() throws Exception { final BasicDataSource ds = new BasicDataSource(getClass().getName() + ".createNonJtaDs"); ds.setDriverClassName("org.hsqldb.jdbcDriver"); ds.setUrl("jdbc:hsqldb:mem:JpaTest"); ds.setUsername("sa"); ds.setPassword(""); ds.setMaxTotal(100); ds.setMaxWait(10000); ds.setTestOnBorrow(true); return ds; } private static void set(final Object instance, final String parameterName, final Class type, final Object value) throws Exception { try { instance.getClass().getMethod("set" + parameterName, type).invoke(instance, value); } catch (final InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { throw e; } } } private static void invoke(final Object instance, final String methodName) throws Exception { try { instance.getClass().getMethod(methodName).invoke(instance); } catch (final InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { throw e; } } } private static void close(final Statement statement) { if (statement == null) { return; } try { statement.close(); } catch (final SQLException e) { } } private static void close(final Connection connection) { if (connection == null) { return; } try { connection.close(); } catch (final SQLException e) { } } private class ControllableTransformer implements ClassFileTransformer { private final ClassFileTransformer transformer; public ControllableTransformer(final ClassFileTransformer transformer) { this.transformer = transformer; } public byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { if (enhance) { return transformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); } else { return null; } } } public class FilteredChildFirstClassLoader extends URLClassLoader { protected String packagePrefix; public FilteredChildFirstClassLoader(final ClassLoader parent, final String packagePrefix) { super(new URL[0], parent); this.packagePrefix = packagePrefix; } public Class loadClass(final String name) throws ClassNotFoundException { return loadClass(name, false); } protected synchronized Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { // see if we've already loaded it final Class c = findLoadedClass(name); if (c != null) { return c; } if (!name.startsWith(packagePrefix)) { return Class.forName(name, resolve, getParent()); } final String resourceName = name.replace('.', '/') + ".class"; final InputStream in = getResourceAsStream(resourceName); if (in == null) { throw new ClassNotFoundException(name); } // 80% of class files are smaller then 6k final ByteArrayOutputStream bout = new ByteArrayOutputStream(8 * 1024); // copy the input stream into a byte array byte[] bytes = new byte[0]; try { final byte[] buf = new byte[4 * 1024]; for (int count = -1; (count = in.read(buf)) >= 0; ) { bout.write(buf, 0, count); } bytes = bout.toByteArray(); } catch (final IOException e) { throw new ClassNotFoundException(name, e); } // define the package final int packageEndIndex = name.lastIndexOf('.'); if (packageEndIndex != -1) { final String packageName = name.substring(0, packageEndIndex); if (getPackage(packageName) == null) { definePackage(packageName, null, null, null, null, null, null, null); } } // define the class try { return defineClass(name, bytes, 0, bytes.length); } catch (final SecurityException e) { // possible prohibited package: defer to the parent return super.loadClass(name, resolve); } } } }