Added: 
aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManager.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManager.java?rev=1737958&view=auto
==============================================================================
--- 
aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManager.java
 (added)
+++ 
aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManager.java
 Wed Apr  6 10:06:08 2016
@@ -0,0 +1,109 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import static 
org.osgi.service.transaction.control.TransactionStatus.NO_TRANSACTION;
+
+import java.util.UUID;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.PersistenceException;
+
+import org.apache.aries.tx.control.jpa.common.impl.EntityManagerWrapper;
+import org.apache.aries.tx.control.jpa.common.impl.ScopedEntityManagerWrapper;
+import org.apache.aries.tx.control.jpa.common.impl.TxEntityManagerWrapper;
+import org.osgi.service.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.TransactionContext;
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.TransactionException;
+
+public class TxContextBindingEntityManager extends EntityManagerWrapper {
+
+       private final TransactionControl        txControl;
+       private final UUID                                      resourceId;
+       private final EntityManagerFactory      emf;
+
+       public TxContextBindingEntityManager(TransactionControl txControl,
+                       EntityManagerFactory emf, UUID resourceId) {
+               this.txControl = txControl;
+               this.emf = emf;
+               this.resourceId = resourceId;
+       }
+
+       @Override
+       protected final EntityManager getRealEntityManager() {
+
+               TransactionContext txContext = txControl.getCurrentContext();
+
+               if (txContext == null) {
+                       throw new TransactionException("The resource " + emf
+                                       + " cannot be accessed outside of an 
active Transaction Context");
+               }
+
+               EntityManager existing = (EntityManager) 
txContext.getScopedValue(resourceId);
+
+               if (existing != null) {
+                       return existing;
+               }
+
+               EntityManager toReturn;
+               EntityManager toClose;
+
+               try {
+                       if (txContext.getTransactionStatus() == NO_TRANSACTION) 
{
+                               toClose = emf.createEntityManager();
+                               toReturn = new 
ScopedEntityManagerWrapper(toClose);
+                       } else if (txContext.supportsLocal()) {
+                               toClose = emf.createEntityManager();
+                               toReturn = new TxEntityManagerWrapper(toClose);
+                               
txContext.registerLocalResource(getLocalResource(toClose));
+                               toClose.getTransaction().begin();
+                       } else {
+                               throw new TransactionException(
+                                               "There is a transaction active, 
but it does not support local participants");
+                       }
+               } catch (Exception sqle) {
+                       throw new TransactionException(
+                                       "There was a problem getting hold of a 
database connection",
+                                       sqle);
+               }
+
+               
+               txContext.postCompletion(x -> {
+                               try {
+                                       toClose.close();
+                               } catch (PersistenceException sqle) {
+                                       // TODO log this
+                               }
+                       });
+               
+               txContext.putScopedValue(resourceId, toReturn);
+               
+               return toReturn;
+       }
+
+       
+       private LocalResource getLocalResource(EntityManager em) {
+               return new LocalResource() {
+                       @Override
+                       public void commit() throws TransactionException {
+                               try {
+                                       em.getTransaction().commit();
+                               } catch (PersistenceException e) {
+                                       throw new TransactionException(
+                                                       "An error occurred when 
committing the connection", e);
+                               }
+                       }
+
+                       @Override
+                       public void rollback() throws TransactionException {
+                               try {
+                                       em.getTransaction().rollback();
+                               } catch (PersistenceException e) {
+                                       throw new TransactionException(
+                                                       "An error occurred when 
rolling back the connection", e);
+                               }
+                       }
+
+               };
+       }
+}

Added: 
aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java?rev=1737958&view=auto
==============================================================================
--- 
aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
 (added)
+++ 
aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
 Wed Apr  6 10:06:08 2016
@@ -0,0 +1,152 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+
+import static org.mockito.Mockito.times;
+import static org.osgi.service.transaction.control.TransactionStatus.ACTIVE;
+import static 
org.osgi.service.transaction.control.TransactionStatus.NO_TRANSACTION;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.service.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.TransactionContext;
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.TransactionException;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TxContextBindingEntityManagerTest {
+
+       @Mock
+       TransactionControl control;
+       
+       @Mock
+       TransactionContext context;
+       
+       @Mock
+       EntityManagerFactory emf;
+       
+       @Mock
+       EntityManager rawEm;
+
+       @Mock
+       EntityTransaction et;
+       
+       Map<Object, Object> variables = new HashMap<>();
+       
+       UUID id = UUID.randomUUID();
+       
+       TxContextBindingEntityManager em;
+       
+       @Before
+       public void setUp() throws SQLException {
+               
Mockito.when(emf.createEntityManager()).thenReturn(rawEm).thenReturn(null);
+               
+               Mockito.when(rawEm.getTransaction()).thenReturn(et);
+               
+               Mockito.doAnswer(i -> variables.put(i.getArguments()[0], 
i.getArguments()[1]))
+                       .when(context).putScopedValue(Mockito.any(), 
Mockito.any());
+               Mockito.when(context.getScopedValue(Mockito.any()))
+                       .thenAnswer(i -> variables.get(i.getArguments()[0]));
+               
+               em = new TxContextBindingEntityManager(control, emf, id);
+       }
+       
+       private void setupNoTransaction() {
+               Mockito.when(control.getCurrentContext()).thenReturn(context);
+               
Mockito.when(context.getTransactionStatus()).thenReturn(NO_TRANSACTION);
+       }
+
+       private void setupActiveTransaction() {
+               Mockito.when(control.getCurrentContext()).thenReturn(context);
+               Mockito.when(context.supportsLocal()).thenReturn(true);
+               Mockito.when(context.getTransactionStatus()).thenReturn(ACTIVE);
+       }
+       
+       
+       @Test(expected=TransactionException.class)
+       public void testUnscoped() throws SQLException {
+               em.isOpen();
+       }
+
+       @Test
+       public void testNoTransaction() throws SQLException {
+               setupNoTransaction();
+               
+               em.isOpen();
+               em.isOpen();
+               
+               Mockito.verify(rawEm, times(2)).isOpen();
+               Mockito.verify(rawEm, times(0)).getTransaction();
+               Mockito.verify(context, 
times(0)).registerLocalResource(Mockito.any());
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+       }
+
+       @Test
+       public void testActiveTransactionCommit() throws SQLException {
+               setupActiveTransaction();
+               
+               em.isOpen();
+               em.isOpen();
+               
+               ArgumentCaptor<LocalResource> captor = 
ArgumentCaptor.forClass(LocalResource.class);
+
+               Mockito.verify(rawEm, times(2)).isOpen();
+               Mockito.verify(et).begin();
+               Mockito.verify(et, times(0)).commit();
+               Mockito.verify(et, times(0)).rollback();
+               Mockito.verify(context).registerLocalResource(captor.capture());
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+               
+               captor.getValue().commit();
+               
+               Mockito.verify(et).commit();
+               Mockito.verify(et, times(0)).rollback();
+       }
+
+       @Test
+       public void testActiveTransactionRollback() throws SQLException {
+               setupActiveTransaction();
+               
+               em.isOpen();
+               em.isOpen();
+               
+               ArgumentCaptor<LocalResource> captor = 
ArgumentCaptor.forClass(LocalResource.class);
+               
+               Mockito.verify(rawEm, times(2)).isOpen();
+               Mockito.verify(et).begin();
+               Mockito.verify(et, times(0)).commit();
+               Mockito.verify(et, times(0)).rollback();
+               Mockito.verify(context).registerLocalResource(captor.capture());
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+               
+               captor.getValue().rollback();
+               
+               Mockito.verify(et).rollback();
+               Mockito.verify(et, times(0)).commit();
+       }
+
+       @Test(expected=TransactionException.class)
+       public void testActiveTransactionNoLocal() throws SQLException {
+               setupActiveTransaction();
+               
+               Mockito.when(context.supportsLocal()).thenReturn(false);
+               em.isOpen();
+       }
+
+}


Reply via email to