You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ti...@apache.org on 2018/03/29 17:23:18 UTC

[aries-tx-control] branch master updated: Make sure that DataSources are recreated when configuration-driven JPA providers lose and re-acquire the EMFBuilder service

This is an automated email from the ASF dual-hosted git repository.

timothyjward pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/aries-tx-control.git


The following commit(s) were added to refs/heads/master by this push:
     new ad0dec7  Make sure that DataSources are recreated when configuration-driven JPA providers lose and re-acquire the EMFBuilder service
ad0dec7 is described below

commit ad0dec7bf264cab86996bf98d951fec001e8ea92
Author: Tim Ward <ti...@apache.org>
AuthorDate: Thu Mar 29 16:53:56 2018 +0100

    Make sure that DataSources are recreated when configuration-driven JPA providers lose and re-acquire the EMFBuilder service
---
 .../control/itests/AbstractJPATransactionTest.java |   9 +-
 .../aries/tx/control/itests/JPALifecycleTest.java  |  25 ++++-
 .../impl/AbstractManagedJPADataSourceSetup.java    |  12 ++-
 .../common/impl/AbstractManagedJPAEMFLocator.java  |  17 ++-
 .../impl/DelayedJPAEntityManagerProvider.java      |  10 +-
 .../impl/JPAEntityManagerProviderFactoryImpl.java  |   2 +-
 .../jpa/local/impl/LocalJPADataSourceSetup.java    |   4 +-
 .../control/jpa/local/impl/LocalJPAEMFLocator.java |   6 +-
 .../local/impl/LocalJPAManagedServiceFactory.java  |   2 +-
 .../jpa/local/impl/LocalJPAEMFLocatorTest.java     | 114 +++++++++++++++++++++
 .../impl/JPAEntityManagerProviderFactoryImpl.java  |   2 +-
 .../control/jpa/xa/impl/XAJPADataSourceSetup.java  |   6 +-
 .../tx/control/jpa/xa/impl/XAJPAEMFLocator.java    |   8 +-
 .../jpa/xa/impl/XAJPAManagedServiceFactory.java    |   2 +-
 .../control/jpa/xa/impl/XAJPAEMFLocatorTest.java   | 114 +++++++++++++++++++++
 15 files changed, 304 insertions(+), 29 deletions(-)

diff --git a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
index 34a911f..a33c17e 100644
--- a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
+++ b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
@@ -94,12 +94,15 @@ public abstract class AbstractJPATransactionTest {
 	protected final List<ServiceTracker<?,?>> trackers = new ArrayList<>();
 	@Before
 	public void setUp() throws Exception {
-		
-		txControl = getService(TransactionControl.class, System.getProperty(TX_CONTROL_FILTER), 5000);
-		
 		server = Server.createTcpServer("-tcpPort", "0");
 		server.start();
 		
+		populateTxEntityManager();
+	}
+
+	protected void populateTxEntityManager() throws Exception {
+		txControl = getService(TransactionControl.class, System.getProperty(TX_CONTROL_FILTER), 5000);
+
 		String jdbcUrl = "jdbc:h2:tcp://127.0.0.1:" + server.getPort() + "/" + getRemoteDBPath();
 		
 		boolean configuredProvider = isConfigured();
diff --git a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
index 23b825e..37b1603 100644
--- a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
+++ b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
@@ -48,8 +48,10 @@ import org.osgi.framework.BundleException;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.transaction.control.ScopedWorkException;
+import org.osgi.service.transaction.control.TransactionControl;
 import org.osgi.service.transaction.control.TransactionException;
 import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
 import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
 
 @RunWith(PaxExam.class)
@@ -99,18 +101,24 @@ public class JPALifecycleTest extends AbstractJPATransactionTest {
 	}
 
 	@Test
-	public void testStopOfTxControlBundle() {
+	public void testStopOfTxControlBundle()  throws Exception {
 		doBundleStoppingTest(b -> b.getSymbolicName().contains("tx-control-service"),
 				"The transaction control service is closed");
 	}
 
 	@Test
-	public void testStopOfJPABundle() {
+	public void testStopOfJPABundle() throws Exception {
 		doBundleStoppingTest(b -> b.getSymbolicName().contains("tx-control-provider-jpa"),
 				"There was a problem getting hold of a database connection");
 	}
 
-	private void doBundleStoppingTest(Predicate<Bundle> p, String exceptionMessage) {
+	@Test
+	public void testStopOfJPAService() throws Exception {
+		doBundleStoppingTest(b -> b.getSymbolicName().contains("org.apache.aries.jpa.container"),
+				"There was a problem getting hold of a database connection");
+	}
+
+	private void doBundleStoppingTest(Predicate<Bundle> p, String exceptionMessage) throws Exception {
 		Message m = new Message();
 		m.message = "Hello World";
 		txControl.required(() -> {em.persist(m); return null;});
@@ -149,6 +157,17 @@ public class JPALifecycleTest extends AbstractJPATransactionTest {
 			});
 			getService(JPAEntityManagerProviderFactory.class, 5000);
 		}
+		if(isConfigured()) {
+			txControl = getService(TransactionControl.class, 5000);
+			em = getService(JPAEntityManagerProvider.class, 5000).getResource(txControl);
+		} else {
+			populateTxEntityManager();
+		}
+			
+		m.id = null;
+		txControl.required(() -> {em.persist(m); return null;});
+
+		assertEquals(m.message, txControl.notSupported(() -> em.find(Message.class, m.id).message));
 	}
 
 	@Test
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
index 1324a39..d87ef91 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
@@ -27,6 +27,8 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
 import org.osgi.framework.BundleContext;
@@ -90,11 +92,11 @@ public abstract class AbstractManagedJPADataSourceSetup implements LifecycleAwar
 		DataSourceFactory service = context.getService(reference);
 		AbstractManagedJPAEMFLocator toReturn;
 		try {
-			Map<String, Object> jpaProps = decorateJPAProperties(service, 
+			Supplier<Map<String, Object>> jpaPropsProvider = () -> decorateJPAProperties(service, 
 					unmodifiableMap(providerProperties), (Properties) jdbcProperties.clone(), 
 					new HashMap<>(baseJPAProperties));
-			toReturn = getManagedJPAEMFLocator(context, pid, jpaProps, providerProperties, 
-					() -> cleanupOnClose(jpaProps));
+			toReturn = getManagedJPAEMFLocator(context, pid, jpaPropsProvider, providerProperties, 
+					jpaProps -> cleanupOnClose(jpaProps));
 		} catch (Exception e) {
 			LOG.error("An error occured creating the Resource provider for pid {}", pid, e);
 			return null;
@@ -106,12 +108,12 @@ public abstract class AbstractManagedJPADataSourceSetup implements LifecycleAwar
 
 	protected abstract Map<String, Object> decorateJPAProperties(DataSourceFactory dsf, 
 			Map<String, Object> providerProperties, Properties jdbcProperties,
-			Map<String, Object> jpaProperties) throws Exception;
+			Map<String, Object> jpaProperties);
 	
 	protected abstract void cleanupOnClose(Map<String, Object> jpaProperties);
 
 	protected abstract AbstractManagedJPAEMFLocator getManagedJPAEMFLocator(BundleContext context, String pid, 
-			Map<String, Object> jpaProps, Map<String, Object> providerProperties, Runnable onClose) throws Exception;
+			Supplier<Map<String, Object>> jpaProps, Map<String, Object> providerProperties, Consumer<Map<String, Object>> onClose) throws Exception;
 
 	private void updateService(ServiceReference<DataSourceFactory> reference, AbstractManagedJPAEMFLocator locator) {
 		boolean setDsf;
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPAEMFLocator.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPAEMFLocator.java
index b40b30d..acd27c0 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPAEMFLocator.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPAEMFLocator.java
@@ -31,7 +31,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 import javax.persistence.EntityManagerFactory;
 
@@ -59,9 +61,9 @@ public abstract class AbstractManagedJPAEMFLocator implements LifecycleAware,
 	
 	private final BundleContext context;
 	private final String pid;
-	private final Map<String, Object> jpaProperties;
+	private final Supplier<Map<String, Object>> jpaProperties;
 	private final Map<String, Object> providerProperties;
-	private final Runnable onClose;
+	private final Consumer<Map<String, Object>> onClose;
 	private final ServiceTracker<EntityManagerFactoryBuilder, EntityManagerFactoryBuilder> emfBuilderTracker;
 
 	private final AtomicReference<EntityManagerFactoryBuilder> activeEMFB = new AtomicReference<>();
@@ -69,8 +71,8 @@ public abstract class AbstractManagedJPAEMFLocator implements LifecycleAware,
 	
 	private final AtomicReference<ServiceRegistration<JPAEntityManagerProvider>> serviceReg = new AtomicReference<>();
 
-	public AbstractManagedJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
-			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
+	public AbstractManagedJPAEMFLocator(BundleContext context, String pid, Supplier<Map<String, Object>> jpaProperties,
+			Map<String, Object> providerProperties, Consumer<Map<String, Object>> onClose) throws InvalidSyntaxException, ConfigurationException {
 		this.context = context;
 		this.pid = pid;
 		this.jpaProperties = jpaProperties;
@@ -176,8 +178,10 @@ public abstract class AbstractManagedJPAEMFLocator implements LifecycleAware,
 
 		if (setEMFB) {
 			AbstractJPAEntityManagerProvider provider = null;
+			Map<String, Object> jpaProps = jpaProperties.get();
+			Runnable close = () -> onClose.accept(jpaProps);
 			try {
-				provider = getResourceProvider(context, service, reference, jpaProperties, providerProperties, onClose);
+				provider = getResourceProvider(context, service, reference, jpaProps, providerProperties, close);
 				providerObject.set(provider);
 				ServiceRegistration<JPAEntityManagerProvider> reg = context
 						.registerService(JPAEntityManagerProvider.class, provider, getServiceProperties());
@@ -189,6 +193,9 @@ public abstract class AbstractManagedJPAEMFLocator implements LifecycleAware,
 				activeEMFB.compareAndSet(service, null);
 				if(provider != null) {
 					provider.close();
+				} else {
+					// Make sure that we don't leak the DataSource
+					close.run();
 				}
 					
 			}
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DelayedJPAEntityManagerProvider.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DelayedJPAEntityManagerProvider.java
index 71a70f5..d3245f1 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DelayedJPAEntityManagerProvider.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DelayedJPAEntityManagerProvider.java
@@ -31,14 +31,18 @@ public class DelayedJPAEntityManagerProvider extends AbstractJPAEntityManagerPro
 	
 	private final ThreadLocal<TransactionControl> commonStore = new ThreadLocal<>();
 	
+	private final Runnable unusedClose;
+
 	private AbstractJPAEntityManagerProvider delegate;
 	
 	private boolean closed;
+
 	
 	public DelayedJPAEntityManagerProvider(Function<ThreadLocal<TransactionControl>, 
-			AbstractJPAEntityManagerProvider> wireToTransactionControl) {
+			AbstractJPAEntityManagerProvider> wireToTransactionControl, Runnable unusedClose) {
 		super(null, null);
 		this.wireToTransactionControl = wireToTransactionControl;
+		this.unusedClose = unusedClose;
 	}
 
 	@Override
@@ -62,11 +66,15 @@ public class DelayedJPAEntityManagerProvider extends AbstractJPAEntityManagerPro
 				closed = true;
 				toClose = delegate;
 				delegate = null;
+			} else {
+				return;
 			}
 		}
 		
 		if(toClose != null) {
 			toClose.close();
+		} else {
+			unusedClose.run();
 		}
 	}
 
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
index d02a3b3..b93d7e7 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
@@ -108,7 +108,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 						emf.close();
 					});
 			};
-		return new DelayedJPAEntityManagerProvider(create);
+		return new DelayedJPAEntityManagerProvider(create, () -> {});
 	}
 
 	public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, 
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
index c77f38b..f831e57 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
@@ -26,6 +26,8 @@ import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFa
 import java.sql.SQLException;
 import java.util.Map;
 import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 import javax.sql.DataSource;
 
@@ -81,7 +83,7 @@ public class LocalJPADataSourceSetup extends AbstractManagedJPADataSourceSetup {
 
 	@Override
 	protected AbstractManagedJPAEMFLocator getManagedJPAEMFLocator(BundleContext context, String pid,
-			Map<String, Object> jpaProps, Map<String, Object> providerProperties, Runnable onClose) 
+			Supplier<Map<String, Object>> jpaProps, Map<String, Object> providerProperties, Consumer<Map<String, Object>> onClose) 
 					throws InvalidSyntaxException, ConfigurationException {
 		return new LocalJPAEMFLocator(context, pid, jpaProps, providerProperties, onClose);
 	}
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocator.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocator.java
index ad2364b..990c44f 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocator.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocator.java
@@ -19,6 +19,8 @@
 package org.apache.aries.tx.control.jpa.local.impl;
 
 import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
 import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
@@ -30,8 +32,8 @@ import org.osgi.service.jpa.EntityManagerFactoryBuilder;
 
 public class LocalJPAEMFLocator extends AbstractManagedJPAEMFLocator {
 
-	public LocalJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
-			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
+	public LocalJPAEMFLocator(BundleContext context, String pid, Supplier<Map<String, Object>> jpaProperties,
+			Map<String, Object> providerProperties, Consumer<Map<String, Object>> onClose) throws InvalidSyntaxException, ConfigurationException {
 		super(context, pid, jpaProperties, providerProperties, onClose);
 	}
 
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAManagedServiceFactory.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAManagedServiceFactory.java
index e5df72d..3730208 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAManagedServiceFactory.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAManagedServiceFactory.java
@@ -50,7 +50,7 @@ public class LocalJPAManagedServiceFactory extends AbstractJPAManagedServiceFact
 	@Override
 	protected AbstractManagedJPAEMFLocator emfTracking(BundleContext context, String pid, Map<String, Object> properties,
 			Map<String, Object> jpaProps) throws InvalidSyntaxException, ConfigurationException {
-		return new LocalJPAEMFLocator(context, pid, jpaProps, properties, null);
+		return new LocalJPAEMFLocator(context, pid, () -> jpaProps, properties, null);
 	}
 
 	@Override
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocatorTest.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocatorTest.java
new file mode 100644
index 0000000..099e170
--- /dev/null
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocatorTest.java
@@ -0,0 +1,114 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import static java.util.Collections.newSetFromMap;
+import static java.util.Collections.singletonMap;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyMapOf;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+import static org.osgi.framework.FrameworkUtil.createFilter;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LocalJPAEMFLocatorTest {
+	
+	private static final String PID = "some.pid";
+	private static final String UNIT_NAME = "foo";
+	
+	private final Set<Map<String, Object>> maps = newSetFromMap(new IdentityHashMap<>());
+	
+	private final Semaphore getJDBCSem = new Semaphore(0);
+	
+	private final Semaphore closeSem = new Semaphore(0);
+	
+	@Mock
+	BundleContext ctx;
+	
+	@Mock
+	ServiceReference<EntityManagerFactoryBuilder> ref;
+
+	@Mock
+	EntityManagerFactoryBuilder emfb;
+
+	@Mock
+	EntityManagerFactory emf;
+	
+	Supplier<Map<String, Object>> propertiesProvider;
+
+	Consumer<Map<String, Object>> closeHandler;
+
+	private LocalJPAEMFLocator locator;
+	
+	@Before
+	public void setUp() throws InvalidSyntaxException, ConfigurationException {
+
+		propertiesProvider = () -> {
+				Map<String, Object> m = new HashMap<>();
+				maps.add(m);
+				getJDBCSem.release();
+				return m;
+			};
+		
+		closeHandler	 = m -> {
+				if(maps.remove(m)) {
+					closeSem.release();
+				}
+			};
+			
+		when(emfb.createEntityManagerFactory(anyMapOf(String.class, Object.class)))
+			.thenReturn(emf);
+		
+		when(ctx.getService(ref)).thenReturn(emfb);
+		
+		when(ctx.createFilter(anyString()))
+			.then(i -> createFilter(i.getArguments()[0].toString()));
+			
+		locator = new LocalJPAEMFLocator(ctx, PID, propertiesProvider, 
+				singletonMap("osgi.unit.name", UNIT_NAME), closeHandler);
+	}
+	
+	@Test
+	public void testEMFAddedRemovedThenAddedAgain() {
+		
+		locator.addingService(ref);
+		
+		assertEquals(1, getJDBCSem.availablePermits());
+		assertEquals(0, closeSem.availablePermits());
+		
+		locator.removedService(ref, emfb);
+		
+		assertEquals(1, getJDBCSem.availablePermits());
+		assertEquals(1, closeSem.availablePermits());
+		
+		locator.addingService(ref);
+		
+		assertEquals(2, getJDBCSem.availablePermits());
+		assertEquals(1, closeSem.availablePermits());
+		
+		locator.removedService(ref, emfb);
+		
+		assertEquals(2, getJDBCSem.availablePermits());
+		assertEquals(2, closeSem.availablePermits());
+	}
+
+}
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java
index c171faf..a9094b5 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java
@@ -104,7 +104,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 			create = handleNormalDataSource(emfb, resourceProviderPropsToUse, jpaPropsToUse);
 		}
 			
-		return new DelayedJPAEntityManagerProvider(create);
+		return new DelayedJPAEntityManagerProvider(create, () -> {});
 	}
 
 	private Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> handleJDBCResourceProvider(
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
index e6cb6cf..b773616 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
@@ -25,6 +25,8 @@ import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFa
 import java.sql.SQLException;
 import java.util.Map;
 import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 import javax.sql.DataSource;
 
@@ -51,7 +53,7 @@ public class XAJPADataSourceSetup extends AbstractManagedJPADataSourceSetup {
 
 	@Override
 	protected Map<String, Object> decorateJPAProperties(DataSourceFactory dsf, Map<String, Object> providerProperties,
-			Properties jdbcProperties, Map<String, Object> jpaProperties) throws Exception {
+			Properties jdbcProperties, Map<String, Object> jpaProperties) {
 		DataSource unpooled;
 		try {
 			if (toBoolean(providerProperties, USE_DRIVER, false)) {
@@ -81,7 +83,7 @@ public class XAJPADataSourceSetup extends AbstractManagedJPADataSourceSetup {
 
 	@Override
 	protected AbstractManagedJPAEMFLocator getManagedJPAEMFLocator(BundleContext context, String pid,
-			Map<String, Object> jpaProps, Map<String, Object> providerProperties, Runnable onClose) throws Exception {
+			Supplier<Map<String, Object>> jpaProps, Map<String, Object> providerProperties, Consumer<Map<String, Object>> onClose) throws Exception {
 		return new XAJPAEMFLocator(context, pid, jpaProps, providerProperties, onClose);
 	}
 }
\ No newline at end of file
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
index 20686d2..12f2ac6 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
@@ -20,6 +20,8 @@ package org.apache.aries.tx.control.jpa.xa.impl;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
 import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
@@ -32,8 +34,8 @@ import org.osgi.service.jpa.EntityManagerFactoryBuilder;
 
 public class XAJPAEMFLocator extends AbstractManagedJPAEMFLocator {
 
-	public XAJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
-			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
+	public XAJPAEMFLocator(BundleContext context, String pid, Supplier<Map<String, Object>> jpaProperties,
+			Map<String, Object> providerProperties, Consumer<Map<String, Object>> onClose) throws InvalidSyntaxException, ConfigurationException {
 		super(context, pid, jpaProperties, providerProperties, onClose);
 	}
 
@@ -48,6 +50,6 @@ public class XAJPAEMFLocator extends AbstractManagedJPAEMFLocator {
 		return new DelayedJPAEntityManagerProvider(t -> {
 			return new JPAEntityManagerProviderFactoryImpl(context).getProviderFor(service,
 					jpaProps, providerProps, t, onClose);
-		});
+		}, onClose);
 	}
 }
\ No newline at end of file
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAManagedServiceFactory.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAManagedServiceFactory.java
index bb70f5d..c17c744 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAManagedServiceFactory.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAManagedServiceFactory.java
@@ -50,7 +50,7 @@ public class XAJPAManagedServiceFactory extends AbstractJPAManagedServiceFactory
 	@Override
 	protected AbstractManagedJPAEMFLocator emfTracking(BundleContext context, String pid, Map<String, Object> properties,
 			Map<String, Object> jpaProps) throws InvalidSyntaxException, ConfigurationException {
-		return new XAJPAEMFLocator(context, pid, jpaProps, properties, null);
+		return new XAJPAEMFLocator(context, pid, () -> jpaProps, properties, null);
 	}
 
 	@Override
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/test/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocatorTest.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/test/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocatorTest.java
new file mode 100644
index 0000000..cc0f546
--- /dev/null
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/test/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocatorTest.java
@@ -0,0 +1,114 @@
+package org.apache.aries.tx.control.jpa.xa.impl;
+
+import static java.util.Collections.newSetFromMap;
+import static java.util.Collections.singletonMap;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyMapOf;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+import static org.osgi.framework.FrameworkUtil.createFilter;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+
+@RunWith(MockitoJUnitRunner.class)
+public class XAJPAEMFLocatorTest {
+	
+	private static final String PID = "some.pid";
+	private static final String UNIT_NAME = "foo";
+	
+	private final Set<Map<String, Object>> maps = newSetFromMap(new IdentityHashMap<>());
+	
+	private final Semaphore getJDBCSem = new Semaphore(0);
+	
+	private final Semaphore closeSem = new Semaphore(0);
+	
+	@Mock
+	BundleContext ctx;
+	
+	@Mock
+	ServiceReference<EntityManagerFactoryBuilder> ref;
+
+	@Mock
+	EntityManagerFactoryBuilder emfb;
+
+	@Mock
+	EntityManagerFactory emf;
+	
+	Supplier<Map<String, Object>> propertiesProvider;
+
+	Consumer<Map<String, Object>> closeHandler;
+
+	private XAJPAEMFLocator locator;
+	
+	@Before
+	public void setUp() throws InvalidSyntaxException, ConfigurationException {
+
+		propertiesProvider = () -> {
+				Map<String, Object> m = new HashMap<>();
+				maps.add(m);
+				getJDBCSem.release();
+				return m;
+			};
+		
+		closeHandler	 = m -> {
+				if(maps.remove(m)) {
+					closeSem.release();
+				}
+			};
+			
+		when(emfb.createEntityManagerFactory(anyMapOf(String.class, Object.class)))
+			.thenReturn(emf);
+		
+		when(ctx.getService(ref)).thenReturn(emfb);
+		
+		when(ctx.createFilter(anyString()))
+			.then(i -> createFilter(i.getArguments()[0].toString()));
+			
+		locator = new XAJPAEMFLocator(ctx, PID, propertiesProvider, 
+				singletonMap("osgi.unit.name", UNIT_NAME), closeHandler);
+	}
+	
+	@Test
+	public void testEMFAddedRemovedThenAddedAgain() {
+		
+		locator.addingService(ref);
+		
+		assertEquals(1, getJDBCSem.availablePermits());
+		assertEquals(0, closeSem.availablePermits());
+		
+		locator.removedService(ref, emfb);
+		
+		assertEquals(1, getJDBCSem.availablePermits());
+		assertEquals(1, closeSem.availablePermits());
+		
+		locator.addingService(ref);
+		
+		assertEquals(2, getJDBCSem.availablePermits());
+		assertEquals(1, closeSem.availablePermits());
+		
+		locator.removedService(ref, emfb);
+		
+		assertEquals(2, getJDBCSem.availablePermits());
+		assertEquals(2, closeSem.availablePermits());
+	}
+
+}

-- 
To stop receiving notification emails like this one, please contact
timothyjward@apache.org.