diff --git a/test/mixins/entitymanager/pom.xml b/test/mixins/entitymanager/pom.xml new file mode 100644 index 000000000..f137326c4 --- /dev/null +++ b/test/mixins/entitymanager/pom.xml @@ -0,0 +1,91 @@ + + + + 4.0.0 + + org.switchyard.components + switchyard-component-test-parent + 2.0.0-SNAPSHOT + ../../pom.xml + + switchyard-component-test-mixin-entitymanager + jar + SwitchYard: Entity Manager MixIn + Entity Manager MixIn classes + http://switchyard.org + + + + + com.experlog + xapool + 1.5.0 + + + + + + + + com.h2database + h2 + test + + + + org.jboss.as + jboss-as-naming + + + org.jboss.logmanager + log4j-jboss-logmanager + + + + + + org.switchyard.components + switchyard-component-test-mixin-cdi + + + org.switchyard.components + switchyard-component-test-mixin-naming + + + org.switchyard.components + switchyard-component-test-mixin-jca + + + org.switchyard.components + switchyard-component-bean + + + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + + + org.hibernate + hibernate-entitymanager + + + + + + com.experlog + xapool + + + diff --git a/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/CDIPersistenceExtension.java b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/CDIPersistenceExtension.java new file mode 100644 index 000000000..a891cd0eb --- /dev/null +++ b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/CDIPersistenceExtension.java @@ -0,0 +1,189 @@ +/* + * CDIPersistenceExtension.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * All rights reserved. + * + * 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.switchyard.component.test.mixins.entitymanager; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AnnotatedConstructor; +import javax.enterprise.inject.spi.AnnotatedField; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.persistence.PersistenceContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Johnathan Ingram (jingram@rogueware.org) + */ +public class CDIPersistenceExtension implements Extension { + + private transient static final Logger log = LoggerFactory.getLogger(CDIPersistenceExtension.class); + + + // Ensure all javax.persistence.EntityManager fields with @PersistenceContext are annotated @Inject + public void processJPAAnnotations(@Observes ProcessAnnotatedType pat) { + final AnnotatedType at = pat.getAnnotatedType(); + + // Override any javax.persistence.EntityManager fields @PersistenceContext annotations with @Inject + AnnotatedType wrapped = new AnnotatedType() { + + @Override + public Class getJavaClass() { + return at.getJavaClass(); + } + + @Override + public Set> getConstructors() { + return at.getConstructors(); + } + + @Override + public Set> getMethods() { + return at.getMethods(); + } + + @Override + public Set> getFields() { + Set> result = new HashSet>(); + for (final AnnotatedField af : at.getFields()) { + // If there is a field with the @PersistenceContext of type javax.persistence.EntityManager, + // make sure the field has the @Inject + if (javax.persistence.EntityManager.class.equals(af.getJavaMember().getType()) + && af.isAnnotationPresent(PersistenceContext.class)) { + result.add(addAnnotation(af, Inject.getAnnotation())); + } else { + result.add(af); + } + } + + return result; + } + + @Override + public Type getBaseType() { + return at.getBaseType(); + } + + @Override + public Set getTypeClosure() { + return at.getTypeClosure(); + } + + @Override + public T getAnnotation(Class annotationType) { + return at.getAnnotation(annotationType); + } + + @Override + public Set getAnnotations() { + return at.getAnnotations(); + } + + @Override + public boolean isAnnotationPresent(Class annotationType) { + return at.isAnnotationPresent(annotationType); + } + }; + + pat.setAnnotatedType(wrapped); + } + + + public static AnnotatedField addAnnotation(final AnnotatedField af, final Annotation... annotations) { + + return new AnnotatedField() { + @Override + public Field getJavaMember() { + return af.getJavaMember(); + } + + @Override + public boolean isStatic() { + return af.isStatic(); + } + + @Override + public AnnotatedType getDeclaringType() { + return af.getDeclaringType(); + } + + @Override + public Type getBaseType() { + return af.getBaseType(); + } + + @Override + public Set getTypeClosure() { + return af.getTypeClosure(); + } + + @Override + public T getAnnotation(Class annotationType) { + for (Annotation a : annotations) { + if (a.annotationType().equals(annotationType)) { + return (T) a; + } + } + return af.getAnnotation(annotationType); + } + + @Override + public Set getAnnotations() { + Set result = new HashSet(af.getAnnotations()); + result.addAll(Arrays.asList(annotations)); + return result; + } + + @Override + public boolean isAnnotationPresent(Class annotationType) { + for (Annotation a : annotations) { + + if (a.annotationType().equals(annotationType)) { + return true; + } + } + + return af.isAnnotationPresent(annotationType); + } + }; + } + + public static class Inject { + + @javax.inject.Inject + public static Object field; + + public static Annotation getAnnotation() { + try { + return Inject.class.getField("field").getAnnotation(javax.inject.Inject.class); + } catch (Exception ex) { + return null; + } + } + } +} diff --git a/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/DataSourceDelegate.java b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/DataSourceDelegate.java new file mode 100644 index 000000000..ca43aff18 --- /dev/null +++ b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/DataSourceDelegate.java @@ -0,0 +1,85 @@ +/* + * DataSourceDelegate.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * + * 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.switchyard.component.test.mixins.entitymanager; + +import java.io.PrintWriter; +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; +import javax.sql.DataSource; + +/** + * + * @author Johnathan Ingram (jingram@rogueware.org) + */ +public class DataSourceDelegate implements javax.sql.DataSource, Serializable { + + private DataSource ds; + + public DataSourceDelegate(DataSource ds) { + this.ds = ds; + } + + @Override + public Connection getConnection() throws SQLException { + return ds.getConnection(); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + return ds.getConnection(username, password); + } + + @Override + public PrintWriter getLogWriter() throws SQLException { + return ds.getLogWriter(); + } + + @Override + public void setLogWriter(PrintWriter out) throws SQLException { + ds.setLogWriter(out); + } + + @Override + public void setLoginTimeout(int seconds) throws SQLException { + ds.setLoginTimeout(seconds); + } + + @Override + public int getLoginTimeout() throws SQLException { + return ds.getLoginTimeout(); + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return ds.getParentLogger(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return ds.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return ds.isWrapperFor(iface); + } +} diff --git a/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerDelegate.java b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerDelegate.java new file mode 100644 index 000000000..b0b9ba7c4 --- /dev/null +++ b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerDelegate.java @@ -0,0 +1,178 @@ +/* + * EntityManagerDelegate.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * + * 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.switchyard.component.test.mixins.entitymanager; + +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import javax.persistence.EntityManager; +import javax.transaction.Status; +import javax.transaction.Synchronization; +import javax.transaction.SystemException; +import javax.transaction.TransactionManager; +import javax.transaction.TransactionRequiredException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Johnathan Ingram (jingram@rogueware.org) + */ +public class EntityManagerDelegate implements InvocationHandler, Serializable { + + private transient static final Logger log = LoggerFactory.getLogger(EntityManagerDelegate.class); + private transient static final ThreadLocal>> threadEntityManagerByTransaction = new ThreadLocal>>(); + + private String unitName; + + public EntityManagerDelegate(String unitName) { + this.unitName = unitName; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + EntityManager em = getEntityManagerForCurrentTransaction(); + try { + return method.invoke(em, args); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + + private EntityManager getEntityManagerForCurrentTransaction() throws SystemException, TransactionRequiredException { + if (null == unitName) { + unitName = EntityManagerMixIn.getDefaultPersistenceUnitName(); + } + long threadId = Thread.currentThread().getId(); + EntityManager em = null; + + // Make sure we have the thread var for this thread managing entity managers + Map> emsPUs = threadEntityManagerByTransaction.get(); + if (null == emsPUs) { + emsPUs = new HashMap>(); + threadEntityManagerByTransaction.set(emsPUs); // Map scoped per thread for each PU, its thread id and em + } + + // Make sure we have a list of thread and em for this pu + Map ems; + if (!emsPUs.containsKey(unitName)) { + ems = new HashMap(); + emsPUs.put(unitName, ems); + } else { + ems = emsPUs.get(unitName); + } + + // If there is a transaction, create new entity manager for transaction or return existing one + TransactionManager tm = EntityManagerMixIn.getTransactionManager(); + if (null != tm && null != tm.getTransaction() && tm.getStatus() != Status.STATUS_NO_TRANSACTION) { + int transactionId = tm.getTransaction().hashCode(); + if (ems.containsKey(transactionId)) { + // return the existing em for the transaction + em = ems.get(transactionId); + log.trace("Returning entity manager for thread id '{}', pu '{}' and transaction id '{}'", threadId, unitName, transactionId); + } else { + // Create an em for the transaction + // Register synchronization to cleanup entity manager when transaction completes + try { + tm.getTransaction().registerSynchronization(new EntityManagerTransactionSynchronization(transactionId)); + } catch (Exception ex) { + log.error("Unable to register transaction synchronization for entity manager cleanup for thread id '{}', pu '{}' and transaction id '{}'", threadId, unitName, transactionId); + throw new IllegalStateException(String.format("Unable to register transaction synchronization for entity manager cleanup for thread id %d, pu %s and transaction id %d", threadId, unitName, transactionId)); + } + em = EntityManagerMixIn.createEntityManager(unitName); + + if (null != em) { + ems.put(transactionId, em); + em.joinTransaction(); + + log.trace("Created entity manager for thread id '{}', pu '{}' and transaction id '{}'", threadId, unitName, transactionId); + } else { + log.warn("Unable to create entity manager for thread id '{}', pu '{}' and transaction id '{}'", threadId, unitName, transactionId); + } + } + } else { + // No transaction + throw new IllegalStateException(String.format("No transaction available to scope enity manager for thread id %d", threadId)); + } + return em; + } + + private class EntityManagerTransactionSynchronization implements Synchronization { + + private final int transactionId; + + protected EntityManagerTransactionSynchronization(int transactionId) { + this.transactionId = transactionId; + } + + @Override + public void beforeCompletion() { + long threadId = Thread.currentThread().getId(); + try { + Map> emsPUs = threadEntityManagerByTransaction.get(); + if (null == emsPUs) { + return; + } + Map ems = emsPUs.get(unitName); + if (null == ems) { + return; + } + + if (ems.containsKey(transactionId)) { + EntityManager em = ems.get(transactionId); + // Make sure nothing further can happen on the entity manager + if (null != em) { + em.close(); + log.trace("Closed entity manager for thread id '{}', pu '{}' and transaction id '{}' on before transaction completion", threadId, unitName, transactionId); + } + } + } catch (Throwable ex) { + log.error("Unknown exception before transaction completion for thread id '{}', pu '{}' and transaction id '{}'", threadId, unitName, transactionId, ex); + } + } + + @Override + public void afterCompletion(int status) { + // Don't care about status, just release the entity manager associated with the transaction + long threadId = Thread.currentThread().getId(); + + try { + Map> emsPUs = threadEntityManagerByTransaction.get(); + if (null == emsPUs) { + return; + } + Map ems = emsPUs.get(unitName); + if (null == ems) { + return; + } + + if (ems.containsKey(transactionId)) { + EntityManager em = ems.remove(transactionId); + em = null; + log.trace("Deleted entity manager for thread id '{}', pu '{}' and transaction id '{}' on after transaction completion", threadId, unitName, transactionId); + } + } catch (Throwable ex) { + log.error("Unknown exception after transaction completion for thread id '{}', pu '{}' and transaction id '{}'", threadId, unitName, transactionId, ex); + } + } + } +} diff --git a/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerMixIn.java b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerMixIn.java new file mode 100644 index 000000000..d9605f869 --- /dev/null +++ b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerMixIn.java @@ -0,0 +1,155 @@ +/* + * EntityManagerMixIn.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * + * 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.switchyard.component.test.mixins.entitymanager; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.naming.InitialContext; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import javax.sql.XADataSource; +import javax.transaction.TransactionManager; +import org.enhydra.jdbc.pool.StandardXAPoolDataSource; +import org.enhydra.jdbc.standard.StandardXADataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.switchyard.component.test.mixins.cdi.CDIMixIn; +import org.switchyard.component.test.mixins.naming.NamingMixIn; +import org.switchyard.component.test.mixins.transaction.TransactionMixIn; +import org.switchyard.test.MixInDependencies; +import org.switchyard.test.mixins.AbstractTestMixIn; + +/** + * + * @author Johnathan Ingram (jingram@rogueware.org) + */ +@MixInDependencies(required = {CDIMixIn.class, NamingMixIn.class, TransactionMixIn.class}) +public class EntityManagerMixIn extends AbstractTestMixIn { + + protected static final Logger log = LoggerFactory.getLogger(EntityManagerMixIn.class); + + private static final Map emfs = Collections.synchronizedMap(new LinkedHashMap()); + private static String primaryPU; + private static TransactionMixIn transactionMixIn; + private static NamingMixIn namingMixIn; + + @Override + public void initialize() { + super.initialize(); + + // Get a reference to the TransactionMixIn + try { + transactionMixIn = getTestKit().getMixIn(TransactionMixIn.class); + } catch (Exception ex) { + log.error("Unable to obtain a reference to the transaction manager mix-in"); + } + + try { + namingMixIn = getTestKit().getMixIn(NamingMixIn.class); + } catch (Exception ex) { + log.error("Unable to obtain a reference to the naming mix-in"); + } + } + + public void createEntityManagerFactory(String persistenceUnitName, String jtaDataSource, String datasourceDriverName, String datasourceUrlr, String datasourceUser, String dataSourcePassword) throws Exception { + // Create a transaction aware XA datasource and bind it to JNDI + try { + StandardXADataSource sXaDs = new StandardXADataSource(); + sXaDs.setUrl(datasourceUrlr); + sXaDs.setDriverName(datasourceDriverName); + sXaDs.setUser(datasourceUser); + sXaDs.setPassword(dataSourcePassword); + sXaDs.setTransactionManager(transactionMixIn.getTransactionManager()); + + StandardXAPoolDataSource xaPoolDS = new StandardXAPoolDataSource(); + xaPoolDS.setTransactionManager(transactionMixIn.getTransactionManager()); // Always set first + xaPoolDS.setDataSource((XADataSource) sXaDs); + xaPoolDS.setUser(datasourceUser); + xaPoolDS.setPassword(dataSourcePassword); + xaPoolDS.setMaxSize(10); + xaPoolDS.setMinSize(5); + xaPoolDS.setDeadLockMaxWait(30 * 1000); + + // Need to bind delegate DataSource + InitialContext initialContext = namingMixIn.getInitialContext(); + initialContext.bind(jtaDataSource, new DataSourceDelegate(xaPoolDS)); + } catch (Exception ex) { + log.error("Unable to create the datasource for JNDI '{}' with driver='{}'", jtaDataSource, datasourceDriverName, ex); + throw ex; + } + + // Create the entity manager factory (EMF) + try { + + EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName); + + emfs.put(persistenceUnitName, emf); + if (null == primaryPU) { + primaryPU = persistenceUnitName; + } + } catch (Exception ex) { + log.error("Unable to create the entity manager factory for persistence unit name '{}'", persistenceUnitName, ex); + throw ex; + } + } + + protected static String getDefaultPersistenceUnitName() { + return primaryPU; + } + + protected static EntityManagerFactory getEntityManagerFactory() { + if (null != primaryPU) { + return emfs.get(primaryPU); + } else { + log.error("Unable to obtain the default entity manager as no entity manager factories have been set with 'createEntityManagerFactory'"); + return null; + } + } + + protected static EntityManagerFactory getEntityManagerFactory(String pu) { + if (emfs.containsKey(pu)) { + return emfs.get(pu); + } else { + log.error("Unable to obtain entity manager for persistence unit '{}' as no entity manager factory has been set", pu); + return null; + } + } + + protected static EntityManager createEntityManager(String persistenceUnitName) { + EntityManagerFactory emf = null; + if (null == (emf = emfs.get(persistenceUnitName))) { + return null; + } + EntityManager em = emf.createEntityManager(); + return em; + } + + protected static TransactionManager getTransactionManager() { + if (null != transactionMixIn || null == transactionMixIn.getTransactionManager()) { + return transactionMixIn.getTransactionManager(); + } else { + log.error("Unable to obtain transaction manager as either transaction mix-in or transaction manager is null"); + return null; + } + } + + +} diff --git a/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerProducer.java b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerProducer.java new file mode 100644 index 000000000..80a6e5000 --- /dev/null +++ b/test/mixins/entitymanager/src/main/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerProducer.java @@ -0,0 +1,67 @@ +/* + * EntityManagerProducer.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * + * 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.switchyard.component.test.mixins.entitymanager; + +import java.io.Serializable; +import java.lang.reflect.Proxy; + +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.inject.Named; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Johnathan Ingram (jingram@rogueware.org) + */ +public class EntityManagerProducer implements Serializable { + + private static final Logger log = LoggerFactory.getLogger(EntityManagerProducer.class); + + @Produces + @Named + public EntityManager createEntityManager(InjectionPoint injectionPoint) { + // Create the delegate to act on behald of the entity manager + // in context of a thread and transaction + String unitName = null != getUnitNameFromAnnotation(injectionPoint) ? getUnitNameFromAnnotation(injectionPoint) : EntityManagerMixIn.getDefaultPersistenceUnitName(); + EntityManagerDelegate emd = new EntityManagerDelegate(unitName); + EntityManager em = (EntityManager) Proxy.newProxyInstance( + EntityManagerDelegate.class + .getClassLoader(), + new Class[]{EntityManager.class}, + emd); + + log.trace("Created entity manager delegate for pu '{}' injected into '{}' class member '{}'", unitName, injectionPoint.getMember().getDeclaringClass().getName(), injectionPoint.getMember().getName()); + return em; + } + + private String getUnitNameFromAnnotation(InjectionPoint injectionPoint) { + PersistenceContext annotation = injectionPoint.getAnnotated().getAnnotation(PersistenceContext.class); + if (null != annotation) { + if (null != annotation.unitName() && 0 != annotation.unitName().length()) { + return annotation.unitName(); + } + } + return null; + } +} diff --git a/test/mixins/entitymanager/src/main/resources/META-INF/beans.xml b/test/mixins/entitymanager/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/test/mixins/entitymanager/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/test/mixins/entitymanager/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 000000000..d90ce883d --- /dev/null +++ b/test/mixins/entitymanager/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +org.switchyard.component.test.mixins.entitymanager.CDIPersistenceExtension \ No newline at end of file diff --git a/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/BasicBean.java b/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/BasicBean.java new file mode 100644 index 000000000..c89cc555b --- /dev/null +++ b/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/BasicBean.java @@ -0,0 +1,70 @@ +/* + * BasicBean.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * + * 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.switchyard.component.test.mixins.entitymanager; + +import javax.enterprise.inject.Default; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; + +/** + * BasicBean + * + * @author rschamm + */ +@Default +public class BasicBean { + + @PersistenceContext + private EntityManager emOne; // should default to mixin-test-one + + @PersistenceContext(unitName = "mixin-test-two") + private EntityManager emTwo; + + public static final String ORIGINAL_NAME = "Peter"; + public static final String UPDATED_NAME_1 = "Peter!!!"; + public static final String UPDATED_NAME_2 = "Peter???"; + + public void testUpdate() throws Exception { + + try + { + // + // Using emOne + // + Query query = emOne.createQuery("update BasicTable set name = :name"); + query.setParameter("name", UPDATED_NAME_1); + query.executeUpdate(); + + // + // Using emTwo + // + query = emTwo.createQuery("update BasicTable set name = :name"); + query.setParameter("name", UPDATED_NAME_2); + query.executeUpdate(); + } + catch (Exception ex) + { + System.out.println("Failed to update name. Error:" + ex.getMessage()); + ex.printStackTrace(); + throw ex; + } + } + +} diff --git a/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/BasicTable.java b/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/BasicTable.java new file mode 100644 index 000000000..04fd7fe69 --- /dev/null +++ b/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/BasicTable.java @@ -0,0 +1,120 @@ +/* + * BasicTable.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * + * 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.switchyard.component.test.mixins.entitymanager; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * BasicTable + * + * @author rschamm + */ +@Entity +@Table(name = "BasicTable") +public class BasicTable implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Column(name = "id") + private Integer id; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 50) + @Column(name = "name") + private String name; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 100) + @Column(name = "description") + private String description; + + public BasicTable() { + } + + public BasicTable(Integer id) { + this.id = id; + } + + public BasicTable(Integer id, String name, String description) { + this.id = id; + this.name = name; + this.description = description; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (id != null ? id.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof BasicTable)) + { + return false; + } + BasicTable other = (BasicTable) object; + if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) + { + return false; + } + return true; + } + + @Override + public String toString() { + return "BasicTable[ id=" + id + " ]"; + } + +} diff --git a/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerMixInTest.java b/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerMixInTest.java new file mode 100644 index 000000000..0017bb4bc --- /dev/null +++ b/test/mixins/entitymanager/src/test/java/org/switchyard/component/test/mixins/entitymanager/EntityManagerMixInTest.java @@ -0,0 +1,160 @@ +/* + * EntityManagerMixInTest.java + * + * Copyright 2014 Johnathan Ingram (jingram@rogueware.org) + * + * 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.switchyard.component.test.mixins.entitymanager; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import javax.inject.Inject; +import org.junit.AfterClass; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.switchyard.component.test.mixins.cdi.CDIMixIn; +import org.switchyard.component.test.mixins.naming.NamingMixIn; +import org.switchyard.component.test.mixins.transaction.TransactionMixIn; +import org.switchyard.test.SwitchYardRunner; +import org.switchyard.test.SwitchYardTestCaseConfig; + +/** + * EntityManagerMixInTest + * + * @author rschamm + */ +@RunWith(SwitchYardRunner.class) +@SwitchYardTestCaseConfig(mixins = +{ + CDIMixIn.class, NamingMixIn.class, TransactionMixIn.class, EntityManagerMixIn.class +}) +public class EntityManagerMixInTest { + + private TransactionMixIn transactionMixIn; + private EntityManagerMixIn entityManagerMixIn; + + private static final String persistenceUnitNameOne = "mixin-test-one"; + private static final String jtaDataSourceOne = "java:jboss/datasources/MixInTestOne"; + private static final String datasourceUrlOne = "jdbc:h2:mem:testDB-one"; + + private static final String persistenceUnitNameTwo = "mixin-test-two"; + private static final String jtaDataSourceTwo = "java:jboss/datasources/MixInTestTwo"; + private static final String datasourceUrlTwo = "jdbc:h2:mem:testDB-two"; + + private static final String datasourceDriverName = "org.h2.Driver"; + private static final String datasourceUser = "sa"; + private static final String dataSourcePassword = "sa"; + + private static Connection connectionOne; + private static Connection connectionTwo; + + @Inject + BasicBean basicBean; + + @BeforeClass + public static void setUp() throws Exception { + connectionOne = DriverManager.getConnection(datasourceUrlOne, datasourceUser, dataSourcePassword); + connectionTwo = DriverManager.getConnection(datasourceUrlTwo, datasourceUser, dataSourcePassword); + } + + @Before + public void setup() throws Exception { + // PU one + entityManagerMixIn.createEntityManagerFactory( + persistenceUnitNameOne, + jtaDataSourceOne, + datasourceDriverName, + datasourceUrlOne, + datasourceUser, + dataSourcePassword); + + // PU two + entityManagerMixIn.createEntityManagerFactory( + persistenceUnitNameTwo, + jtaDataSourceTwo, + datasourceDriverName, + datasourceUrlTwo, + datasourceUser, + dataSourcePassword); + + initSQL(connectionOne); + initSQL(connectionTwo); + } + + @AfterClass + public static void shutDown() throws SQLException { + cleanSQL(connectionOne); + cleanSQL(connectionTwo); + } + + @Test + public void testUpdateCommit() throws Exception { + assertEquals(BasicBean.ORIGINAL_NAME, getName(connectionOne)); + assertEquals(BasicBean.ORIGINAL_NAME, getName(connectionTwo)); + + transactionMixIn.getUserTransaction().begin(); + basicBean.testUpdate(); + transactionMixIn.getUserTransaction().commit(); + + assertEquals(BasicBean.UPDATED_NAME_1, getName(connectionOne)); + assertEquals(BasicBean.UPDATED_NAME_2, getName(connectionTwo)); + } + + @Test + public void testUpdateRollback() throws Exception { + assertEquals(BasicBean.ORIGINAL_NAME, getName(connectionOne)); + assertEquals(BasicBean.ORIGINAL_NAME, getName(connectionTwo)); + + transactionMixIn.getUserTransaction().begin(); + basicBean.testUpdate(); + transactionMixIn.getUserTransaction().rollback(); + + assertEquals(BasicBean.ORIGINAL_NAME, getName(connectionOne)); + assertEquals(BasicBean.ORIGINAL_NAME, getName(connectionTwo)); + } + + private String getName(Connection c) throws SQLException { + String name = null; + String sql = "SELECT name FROM BasicTable"; + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + if (rs.next()) + { + name = rs.getString("name"); + } + rs.close(); + stmt.close(); + return name; + } + + private void initSQL(Connection c) throws SQLException { + c.prepareStatement("DROP TABLE BasicTable IF EXISTS").execute(); + c.prepareStatement("CREATE TABLE BasicTable( ID BIGINT, NAME VARCHAR2(50), DESCRIPTION VARCHAR2(100))").execute(); + c.prepareStatement("INSERT INTO BasicTable (id, name, description) VALUES (1, '" + BasicBean.ORIGINAL_NAME + "', 'Description')").execute(); + } + + private static void cleanSQL(Connection c) throws SQLException { + if (!c.isClosed()) + { + c.close(); + } + } +} diff --git a/test/mixins/entitymanager/src/test/resources/META-INF/beans.xml b/test/mixins/entitymanager/src/test/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/test/mixins/entitymanager/src/test/resources/META-INF/persistence.xml b/test/mixins/entitymanager/src/test/resources/META-INF/persistence.xml new file mode 100644 index 000000000..50cf33567 --- /dev/null +++ b/test/mixins/entitymanager/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,25 @@ + + + + org.hibernate.ejb.HibernatePersistence + java:jboss/datasources/MixInTestOne + org.switchyard.component.test.mixins.entitymanager.BasicTable + true + + + + + + + + org.hibernate.ejb.HibernatePersistence + java:jboss/datasources/MixInTestTwo + org.switchyard.component.test.mixins.entitymanager.BasicTable + true + + + + + + + diff --git a/test/pom.xml b/test/pom.xml index 61a1528ca..17993f4d0 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -34,6 +34,7 @@ mixins/http mixins/jca mixins/smooks + mixins/entitymanager