[ARIES-1585] Add tests for JDBC connection cleanup, and fix the leaks!

git-svn-id: https://svn.apache.org/repos/asf/aries/trunk@1753813 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tx-control/tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractTransactionTest.java b/tx-control/tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractTransactionTest.java
index e7e340c..ef5542c 100644
--- a/tx-control/tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractTransactionTest.java
+++ b/tx-control/tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractTransactionTest.java
@@ -76,7 +76,7 @@
 

 	private Server server;

 	

-	private final List<ServiceTracker<?,?>> trackers = new ArrayList<>();

+	protected final List<ServiceTracker<?,?>> trackers = new ArrayList<>();

 

 	@Before

 	public void setUp() throws Exception {

@@ -100,7 +100,7 @@
 		

 		jdbc.setProperty(DataSourceFactory.JDBC_URL, jdbcUrl);

 		

-		boolean configuredProvider = System.getProperties().containsKey(CONFIGURED_PROVIDER_PROPERTY);

+		boolean configuredProvider = isConfigured();

 		

 		connection = configuredProvider ? configuredConnection(jdbc) : programaticConnection(jdbc);

 		

@@ -114,7 +114,11 @@
 			});

 	}

 

-	private <T> T getService(Class<T> clazz, long timeout) {

+	public boolean isConfigured() {

+		return System.getProperties().containsKey(CONFIGURED_PROVIDER_PROPERTY);

+	}

+

+	protected <T> T getService(Class<T> clazz, long timeout) {

 		try {

 			return getService(clazz, null, timeout);

 		} catch (InvalidSyntaxException e) {

@@ -169,7 +173,7 @@
 		System.out.println("Configuring connection provider with pid " + pid);

 		

 		org.osgi.service.cm.Configuration config = cm.createFactoryConfiguration(

-				pid, null);

+				pid, "?");

 		config.update((Hashtable)jdbc);

 		

 		return getService(JDBCConnectionProvider.class, 5000).getResource(txControl);

@@ -178,11 +182,7 @@
 	@After

 	public void tearDown() {

 

-		txControl.required(() -> connection.createStatement()

-				.execute("DROP TABLE TEST_TABLE"));

-		

-		

-		if(System.getProperties().containsKey(CONFIGURED_PROVIDER_PROPERTY)) {

+		if(isConfigured()) {

 			clearConfiguration();

 		}

 		

diff --git a/tx-control/tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/ConnectionLifecycleTest.java b/tx-control/tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/ConnectionLifecycleTest.java
new file mode 100644
index 0000000..5fa4101
--- /dev/null
+++ b/tx-control/tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/ConnectionLifecycleTest.java
@@ -0,0 +1,231 @@
+/*

+ * 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 WARRANTIESOR 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.aries.tx.control.itests;

+

+import static java.util.stream.Collectors.toList;

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertNotNull;

+import static org.junit.Assert.assertTrue;

+import static org.junit.Assert.fail;

+

+import java.sql.ResultSet;

+import java.util.Arrays;

+import java.util.List;

+import java.util.function.Predicate;

+

+import org.junit.Assume;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.ops4j.pax.exam.junit.PaxExam;

+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;

+import org.ops4j.pax.exam.spi.reactors.PerClass;

+import org.osgi.framework.Bundle;

+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.TransactionException;

+import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;

+

+@RunWith(PaxExam.class)

+@ExamReactorStrategy(PerClass.class)

+public class ConnectionLifecycleTest extends AbstractTransactionTest {

+

+	@Test

+	public void testStopOfTxControlBundle() {

+		doBundleStoppingTest(b -> b.getSymbolicName().contains("tx-control-service"),

+				"The transaction control service is closed");

+	}

+

+	@Test

+	public void testStopOfJDBCBundle() {

+		doBundleStoppingTest(b -> b.getSymbolicName().contains("tx-control-provider-jdbc"), 

+				"There was a problem getting hold of a database connection");

+	}

+

+	private void doBundleStoppingTest(Predicate<Bundle> p, String exceptionMessage) {

+		txControl.required(() -> connection.createStatement()

+				.execute("Insert into TEST_TABLE values ( 'Hello World!' )"));

+

+		assertEquals("Hello World!", txControl.notSupported(() -> {

+			ResultSet rs = connection.createStatement()

+					.executeQuery("Select * from TEST_TABLE");

+			rs.next();

+			return rs.getString(1);

+		}));

+		

+		

+		List<Bundle> toStop = Arrays.stream(context.getBundles())

+			.filter(p)

+			.collect(toList());

+		

+		System.out.println(toStop);

+		

+		try {

+			toStop.stream()

+				.forEach(b -> {

+					System.out.println("Stopping " + b.getSymbolicName());

+					try {

+						b.stop();

+					} catch (BundleException e) {}

+				});

+		

+			try {

+				assertEquals("Hello World!", txControl.notSupported(() -> {

+					ResultSet rs = connection.createStatement()

+							.executeQuery("Select * from TEST_TABLE");

+					rs.next();

+					return rs.getString(1);

+				}));

+				fail("Should not be accessible");

+			} catch (ScopedWorkException swe) {

+				assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);

+				assertEquals(exceptionMessage, swe.getCause().getMessage());

+			} catch (TransactionException te) {

+				assertEquals(exceptionMessage, te.getMessage());

+			}

+		} finally {

+			toStop.stream()

+				.forEach(b -> {

+					try {

+						b.start();

+					} catch (BundleException e) {}

+				});

+		}

+	}

+

+	@Test

+	public void testDeleteOfConfig() throws Exception {

+		Assume.assumeTrue("Not a configuration test", isConfigured());

+		

+		

+		txControl.required(() -> connection.createStatement()

+				.execute("Insert into TEST_TABLE values ( 'Hello World!' )"));

+

+		assertEquals("Hello World!", txControl.notSupported(() -> {

+			ResultSet rs = connection.createStatement()

+					.executeQuery("Select * from TEST_TABLE");

+			rs.next();

+			return rs.getString(1);

+		}));

+		

+		

+		ConfigurationAdmin cm = getService(ConfigurationAdmin.class, 5000);

+		

+		Configuration[] configurations = cm.listConfigurations(

+				"(service.factoryPid=org.apache.aries.tx.control.jdbc.*)");

+		

+		assertNotNull(configurations);

+		assertEquals(1, configurations.length);

+		

+		configurations[0].delete();

+		

+		Thread.sleep(2000);

+		

+		try {

+			assertEquals("Hello World!", txControl.notSupported(() -> {

+				ResultSet rs = connection.createStatement()

+						.executeQuery("Select * from TEST_TABLE");

+				rs.next();

+				return rs.getString(1);

+			}));

+			fail("Should not be accessible");

+		} catch (ScopedWorkException swe) {

+			assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);

+			assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());

+		}

+	}

+

+	@Test

+	public void testUpdateOfConfig() throws Exception {

+		Assume.assumeTrue("Not a configuration test", isConfigured());

+		

+		

+		txControl.required(() -> connection.createStatement()

+				.execute("Insert into TEST_TABLE values ( 'Hello World!' )"));

+

+		assertEquals("Hello World!", txControl.notSupported(() -> {

+			ResultSet rs = connection.createStatement()

+					.executeQuery("Select * from TEST_TABLE");

+			rs.next();

+			return rs.getString(1);

+		}));

+		

+		

+		ConfigurationAdmin cm = getService(ConfigurationAdmin.class, 5000);

+		

+		Configuration[] configurations = cm.listConfigurations(

+				"(service.factoryPid=org.apache.aries.tx.control.jdbc.*)");

+		

+		assertNotNull(configurations);

+		assertEquals(1, configurations.length);

+		

+		configurations[0].update();

+		

+		Thread.sleep(2000);

+		

+		try {

+			assertEquals("Hello World!", txControl.notSupported(() -> {

+				ResultSet rs = connection.createStatement()

+						.executeQuery("Select * from TEST_TABLE");

+				rs.next();

+				return rs.getString(1);

+			}));

+			fail("Should not be accessible");

+		} catch (ScopedWorkException swe) {

+			assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);

+			assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());

+		}

+	}

+	

+	@Test

+	public void testReleaseOfFactoryService() {

+		Assume.assumeFalse("Not a factory test", isConfigured());

+		

+		txControl.required(() -> connection.createStatement()

+				.execute("Insert into TEST_TABLE values ( 'Hello World!' )"));

+

+		assertEquals("Hello World!", txControl.notSupported(() -> {

+			ResultSet rs = connection.createStatement()

+					.executeQuery("Select * from TEST_TABLE");

+			rs.next();

+			return rs.getString(1);

+		}));

+		

+		

+		trackers.stream()

+			.filter(t -> t.getService() instanceof JDBCConnectionProviderFactory)

+			.findFirst()

+			.get().close();;

+		

+		

+		try {

+			assertEquals("Hello World!", txControl.notSupported(() -> {

+				ResultSet rs = connection.createStatement()

+						.executeQuery("Select * from TEST_TABLE");

+				rs.next();

+				return rs.getString(1);

+			}));

+			fail("Should not be accessible");

+		} catch (ScopedWorkException swe) {

+			assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);

+			assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());

+		}

+	}

+}

diff --git a/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/AbstractJDBCConnectionProvider.java b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/AbstractJDBCConnectionProvider.java
new file mode 100644
index 0000000..181a2a1
--- /dev/null
+++ b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/AbstractJDBCConnectionProvider.java
@@ -0,0 +1,55 @@
+/*
+ * 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 WARRANTIESOR 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.aries.tx.control.jdbc.common.impl;
+
+import java.sql.Connection;
+
+import javax.sql.DataSource;
+
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractJDBCConnectionProvider implements JDBCConnectionProvider {
+
+	private static final Logger LOG = LoggerFactory.getLogger(AbstractJDBCConnectionProvider.class);
+	
+	protected final DataSource dataSource;
+	
+	public AbstractJDBCConnectionProvider(DataSource dataSource) {
+		this.dataSource = dataSource;
+	}
+
+	@Override
+	public abstract Connection getResource(TransactionControl txControl)
+			throws TransactionException;
+
+	
+	public void close() {
+		if(dataSource instanceof AutoCloseable) {
+			try {
+				((AutoCloseable) dataSource).close();
+			} catch (Exception e) {
+				LOG.warn("An error occurred shutting down the JDBCConnectionProvider {}", dataSource, e);
+			}
+		}
+	}
+}
diff --git a/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/InternalJDBCConnectionProviderFactory.java b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/InternalJDBCConnectionProviderFactory.java
new file mode 100644
index 0000000..060dbcc
--- /dev/null
+++ b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/InternalJDBCConnectionProviderFactory.java
@@ -0,0 +1,27 @@
+package org.apache.aries.tx.control.jdbc.common.impl;
+
+import java.sql.Driver;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+import javax.sql.XADataSource;
+
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;
+
+public interface InternalJDBCConnectionProviderFactory extends JDBCConnectionProviderFactory {
+
+	@Override
+	AbstractJDBCConnectionProvider getProviderFor(DataSourceFactory dsf, Properties jdbcProperties,
+			Map<String, Object> resourceProviderProperties);
+
+	AbstractJDBCConnectionProvider getProviderFor(DataSource ds, Map<String, Object> resourceProviderProperties);
+
+	@Override
+	AbstractJDBCConnectionProvider getProviderFor(Driver driver, Properties jdbcProperties,
+			Map<String, Object> resourceProviderProperties);
+
+	AbstractJDBCConnectionProvider getProviderFor(XADataSource ds, Map<String, Object> resourceProviderProperties);
+
+}
diff --git a/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/JDBCConnectionProviderFactoryServiceFactory.java b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/JDBCConnectionProviderFactoryServiceFactory.java
new file mode 100644
index 0000000..f577907
--- /dev/null
+++ b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/JDBCConnectionProviderFactoryServiceFactory.java
@@ -0,0 +1,38 @@
+package org.apache.aries.tx.control.jdbc.common.impl;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+public abstract class JDBCConnectionProviderFactoryServiceFactory implements 
+	ServiceFactory<ResourceTrackingJDBCConnectionProviderFactory> {
+
+	Set<ResourceTrackingJDBCConnectionProviderFactory> factories = new CopyOnWriteArraySet<>();
+	
+	@Override
+	public ResourceTrackingJDBCConnectionProviderFactory getService(Bundle bundle,
+			ServiceRegistration<ResourceTrackingJDBCConnectionProviderFactory> registration) {
+		ResourceTrackingJDBCConnectionProviderFactory factory = new ResourceTrackingJDBCConnectionProviderFactory(
+						getInternalJDBCConnectionProviderFactory());
+		factories.add(factory);
+		return factory;
+	}
+
+	@Override
+	public void ungetService(Bundle bundle, ServiceRegistration<ResourceTrackingJDBCConnectionProviderFactory> registration,
+			ResourceTrackingJDBCConnectionProviderFactory service) {
+		factories.remove(service);
+		service.closeAll();
+	}
+	
+	public void close() {
+		factories.stream()
+			.forEach(r -> r.closeAll());
+	}
+	
+	protected abstract InternalJDBCConnectionProviderFactory 
+		getInternalJDBCConnectionProviderFactory();
+}
diff --git a/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/ResourceTrackingJDBCConnectionProviderFactory.java b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/ResourceTrackingJDBCConnectionProviderFactory.java
new file mode 100644
index 0000000..5b87f07
--- /dev/null
+++ b/tx-control/tx-control-provider-jdbc-common/src/main/java/org/apache/aries/tx/control/jdbc/common/impl/ResourceTrackingJDBCConnectionProviderFactory.java
@@ -0,0 +1,91 @@
+package org.apache.aries.tx.control.jdbc.common.impl;
+
+import java.sql.Driver;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Supplier;
+
+import javax.sql.DataSource;
+import javax.sql.XADataSource;
+
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
+import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;
+
+class ResourceTrackingJDBCConnectionProviderFactory implements
+	JDBCConnectionProviderFactory {
+
+	private final List<AbstractJDBCConnectionProvider> toClose = new ArrayList<>();
+	
+	private final InternalJDBCConnectionProviderFactory factory;
+	
+	private boolean closed;
+	
+	public ResourceTrackingJDBCConnectionProviderFactory(InternalJDBCConnectionProviderFactory factory) {
+		this.factory = factory;
+	}
+
+	@Override
+	public JDBCConnectionProvider getProviderFor(DataSourceFactory dsf, Properties jdbcProperties,
+			Map<String, Object> resourceProviderProperties) {
+		return doGetResult(() -> factory.getProviderFor(dsf, 
+				jdbcProperties, resourceProviderProperties));
+	}
+
+	@Override
+	public JDBCConnectionProvider getProviderFor(DataSource ds, Map<String, Object> resourceProviderProperties) {
+		return doGetResult(() -> factory.getProviderFor(ds, 
+				resourceProviderProperties));
+	}
+
+	@Override
+	public JDBCConnectionProvider getProviderFor(Driver driver, Properties jdbcProperties,
+			Map<String, Object> resourceProviderProperties) {
+		return doGetResult(() -> factory.getProviderFor(driver, 
+				jdbcProperties, resourceProviderProperties));
+	}
+
+	@Override
+	public JDBCConnectionProvider getProviderFor(XADataSource ds, Map<String, Object> resourceProviderProperties) {
+		return doGetResult(() -> factory.getProviderFor(ds, 
+				resourceProviderProperties));
+	}
+	
+	private AbstractJDBCConnectionProvider doGetResult(Supplier<AbstractJDBCConnectionProvider> getter) {
+		synchronized (getter) {
+			if (closed) {
+				throw new IllegalStateException("This ResourceProvider has been reclaimed because the factory service that provided it was released");
+			}
+		}
+		AbstractJDBCConnectionProvider ajcp = getter.get();
+		boolean destroy = false;
+		synchronized (toClose) {
+			if (closed) {
+				destroy = true;
+			} else {
+			    toClose.add(ajcp);
+			}
+		}
+		if(destroy) {
+			ajcp.close();
+			throw new IllegalStateException("This ResourceProvider has been reclaimed because the factory service that provided it was released");
+		}
+		return ajcp;
+	}
+
+	public void closeAll() {
+		synchronized (toClose) {
+			closed = true;
+		}
+		// toClose is now up to date and no other thread will write it
+		toClose.stream().forEach(ajcp -> {
+			try {
+				ajcp.close();
+			} catch (Exception e) {}
+		});
+		
+		toClose.clear();
+	}
+}
\ No newline at end of file
diff --git a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/Activator.java b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/Activator.java
index 52693c6..993dbe4 100644
--- a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/Activator.java
+++ b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/Activator.java
@@ -23,6 +23,8 @@
 import java.util.Dictionary;
 import java.util.Hashtable;
 
+import org.apache.aries.tx.control.jdbc.common.impl.InternalJDBCConnectionProviderFactory;
+import org.apache.aries.tx.control.jdbc.common.impl.JDBCConnectionProviderFactoryServiceFactory;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
@@ -31,22 +33,42 @@
 
 public class Activator implements BundleActivator {
 
-	private ServiceRegistration<JDBCConnectionProviderFactory> reg;
+	private ServiceRegistration<?> reg;
 	private ServiceRegistration<ManagedServiceFactory> factoryReg;
+	private JDBCConnectionProviderFactoryServiceFactory service;
+	private ManagedServiceFactoryImpl msf;
 	
 	@Override
 	public void start(BundleContext context) throws Exception {
-		reg = context.registerService(JDBCConnectionProviderFactory.class, 
-				new JDBCConnectionProviderFactoryImpl(), getProperties());
 		
+		InternalJDBCConnectionProviderFactory ijcpf = new JDBCConnectionProviderFactoryImpl();
+		
+		service = new JDBCConnectionProviderFactoryServiceFactory() {
+			@Override
+			protected InternalJDBCConnectionProviderFactory getInternalJDBCConnectionProviderFactory() {
+				return ijcpf;
+			}
+		};
+		reg = context.registerService(JDBCConnectionProviderFactory.class.getName(), 
+				service, getProperties());
+		
+		msf = new ManagedServiceFactoryImpl(context);
 		factoryReg = context.registerService(ManagedServiceFactory.class, 
-				new ManagedServiceFactoryImpl(context), getMSFProperties());
+				msf, getMSFProperties());
 	}
 
 	@Override
 	public void stop(BundleContext context) throws Exception {
-		reg.unregister();
-		factoryReg.unregister();
+		safeUnregister(reg);
+		safeUnregister(factoryReg);
+		service.close();
+		msf.stop();
+	}
+
+	private void safeUnregister(ServiceRegistration<?> reg) {
+		try {
+			reg.unregister();
+		} catch (IllegalStateException ise) {}
 	}
 
 	private Dictionary<String, Object> getProperties() {
diff --git a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderFactoryImpl.java b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderFactoryImpl.java
index b2bc786..4aca90d 100644
--- a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderFactoryImpl.java
+++ b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderFactoryImpl.java
@@ -32,19 +32,20 @@
 import javax.sql.DataSource;
 import javax.sql.XADataSource;
 
+import org.apache.aries.tx.control.jdbc.common.impl.AbstractJDBCConnectionProvider;
 import org.apache.aries.tx.control.jdbc.common.impl.DriverDataSource;
+import org.apache.aries.tx.control.jdbc.common.impl.InternalJDBCConnectionProviderFactory;
 import org.osgi.service.jdbc.DataSourceFactory;
 import org.osgi.service.transaction.control.TransactionException;
-import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
 import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;
 
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 
-public class JDBCConnectionProviderFactoryImpl implements JDBCConnectionProviderFactory {
+public class JDBCConnectionProviderFactoryImpl implements JDBCConnectionProviderFactory, InternalJDBCConnectionProviderFactory {
 
 	@Override
-	public JDBCConnectionProvider getProviderFor(DataSourceFactory dsf, Properties jdbcProperties,
+	public AbstractJDBCConnectionProvider getProviderFor(DataSourceFactory dsf, Properties jdbcProperties,
 			Map<String, Object> resourceProviderProperties) {
 
 		checkEnlistment(resourceProviderProperties);
@@ -67,7 +68,7 @@
 	}
 
 	@Override
-	public JDBCConnectionProvider getProviderFor(DataSource ds, Map<String, Object> resourceProviderProperties) {
+	public AbstractJDBCConnectionProvider getProviderFor(DataSource ds, Map<String, Object> resourceProviderProperties) {
 		checkEnlistment(resourceProviderProperties);
 		DataSource toUse = poolIfNecessary(resourceProviderProperties, ds);
 
@@ -75,7 +76,7 @@
 	}
 
 	@Override
-	public JDBCConnectionProvider getProviderFor(Driver driver, Properties jdbcProperties, 
+	public AbstractJDBCConnectionProvider getProviderFor(Driver driver, Properties jdbcProperties, 
 			Map<String, Object> resourceProviderProperties) {
 		checkEnlistment(resourceProviderProperties);
 		DataSource toUse = poolIfNecessary(resourceProviderProperties, 
@@ -85,7 +86,7 @@
 	}
 
 	@Override
-	public JDBCConnectionProvider getProviderFor(XADataSource ds, Map<String, Object> resourceProviderProperties) {
+	public AbstractJDBCConnectionProvider getProviderFor(XADataSource ds, Map<String, Object> resourceProviderProperties) {
 		checkEnlistment(resourceProviderProperties);
 		
 		DataSource unpooled;
diff --git a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderImpl.java b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderImpl.java
index 996f12a..75b5d73 100644
--- a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderImpl.java
+++ b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/JDBCConnectionProviderImpl.java
@@ -23,18 +23,16 @@
 
 import javax.sql.DataSource;
 
+import org.apache.aries.tx.control.jdbc.common.impl.AbstractJDBCConnectionProvider;
 import org.osgi.service.transaction.control.TransactionControl;
 import org.osgi.service.transaction.control.TransactionException;
-import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
 
-public class JDBCConnectionProviderImpl implements JDBCConnectionProvider {
+public class JDBCConnectionProviderImpl extends AbstractJDBCConnectionProvider {
 
 	private final UUID			uuid	= UUID.randomUUID();
 
-	private final DataSource dataSource;
-	
 	public JDBCConnectionProviderImpl(DataSource dataSource) {
-		this.dataSource = dataSource;
+		super(dataSource);
 	}
 
 	@Override
@@ -42,5 +40,4 @@
 			throws TransactionException {
 		return new TxContextBindingConnection(txControl, dataSource , uuid);
 	}
-
 }
diff --git a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/ManagedServiceFactoryImpl.java b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/ManagedServiceFactoryImpl.java
index db09ac2..f6504a6 100644
--- a/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/ManagedServiceFactoryImpl.java
+++ b/tx-control/tx-control-provider-jdbc-local/src/main/java/org/apache/aries/tx/control/jdbc/local/impl/ManagedServiceFactoryImpl.java
@@ -45,6 +45,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.aries.tx.control.jdbc.common.impl.AbstractJDBCConnectionProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -150,6 +151,7 @@
 
 		private final AtomicReference<DataSourceFactory> activeDsf = new AtomicReference<>();
 		private final AtomicReference<ServiceRegistration<JDBCConnectionProvider>> serviceReg = new AtomicReference<>();
+		private final AtomicReference<AbstractJDBCConnectionProvider> providerObject = new AtomicReference<>();
 
 		public ManagedJDBCResourceProvider(BundleContext context, String pid, Properties jdbcProperties,
 				Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
@@ -197,17 +199,24 @@
 			}
 
 			if (setDsf) {
+				AbstractJDBCConnectionProvider provider = null;
+				ServiceRegistration<JDBCConnectionProvider> reg = null;
 				try {
-					JDBCConnectionProvider provider = new JDBCConnectionProviderFactoryImpl().getProviderFor(service,
+					provider = new JDBCConnectionProviderFactoryImpl().getProviderFor(service,
 							jdbcProperties, providerProperties);
-					ServiceRegistration<JDBCConnectionProvider> reg = context
-							.registerService(JDBCConnectionProvider.class, provider, getServiceProperties());
-					if (!serviceReg.compareAndSet(null, reg)) {
-						throw new IllegalStateException("Unable to set the JDBC connection provider registration");
+					reg = context.registerService(JDBCConnectionProvider.class, 
+							provider, getServiceProperties());
+					synchronized (this) {
+						if (!serviceReg.compareAndSet(null, reg)) {
+							throw new IllegalStateException("Unable to set the JDBC connection provider registration");
+						} else {
+							providerObject.set(provider);
+						}
 					}
 				} catch (Exception e) {
-					LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
 					activeDsf.compareAndSet(service, null);
+					safeUnregister(reg, provider);
+					LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
 				}
 			}
 		}
@@ -229,13 +238,27 @@
 		public void removedService(ServiceReference<DataSourceFactory> reference, DataSourceFactory service) {
 			boolean dsfLeft;
 			ServiceRegistration<JDBCConnectionProvider> oldReg = null;
+			AbstractJDBCConnectionProvider oldProvider = null;
 			synchronized (this) {
 				dsfLeft = activeDsf.compareAndSet(service, null);
 				if (dsfLeft) {
 					oldReg = serviceReg.getAndSet(null);
+					oldProvider = providerObject.getAndSet(null);
 				}
 			}
+			
+			safeUnregister(oldReg, oldProvider);
 
+			if (dsfLeft) {
+				DataSourceFactory newDSF = dsfTracker.getService();
+				if (newDSF != null) {
+					updateService(dsfTracker.getService());
+				}
+			}
+		}
+
+		private void safeUnregister(ServiceRegistration<?> oldReg, 
+				AbstractJDBCConnectionProvider provider) {
 			if (oldReg != null) {
 				try {
 					oldReg.unregister();
@@ -243,11 +266,11 @@
 					LOG.debug("An exception occurred when unregistering a service for {}", pid);
 				}
 			}
-
-			if (dsfLeft) {
-				DataSourceFactory newDSF = dsfTracker.getService();
-				if (newDSF != null) {
-					updateService(dsfTracker.getService());
+			if(provider != null) {
+				try {
+					provider.close();
+				} catch (Exception e) {
+					LOG.debug("An exception occurred when closing a provider for {}", pid, e);
 				}
 			}
 		}
diff --git a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java
index dcfa6cf..56754b8 100644
--- a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java
+++ b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java
@@ -23,6 +23,8 @@
 import java.util.Dictionary;
 import java.util.Hashtable;
 
+import org.apache.aries.tx.control.jdbc.common.impl.InternalJDBCConnectionProviderFactory;
+import org.apache.aries.tx.control.jdbc.common.impl.JDBCConnectionProviderFactoryServiceFactory;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
@@ -33,22 +35,42 @@
 
 	private ServiceRegistration<JDBCConnectionProviderFactory> reg;
 	private ServiceRegistration<ManagedServiceFactory> factoryReg;
+	private JDBCConnectionProviderFactoryServiceFactory service;
+	private ManagedServiceFactoryImpl msf;
 	
 	@Override
 	public void start(BundleContext context) throws Exception {
+		InternalJDBCConnectionProviderFactory ijcpf = new JDBCConnectionProviderFactoryImpl();
+		
+		service = new JDBCConnectionProviderFactoryServiceFactory() {
+			@Override
+			protected InternalJDBCConnectionProviderFactory getInternalJDBCConnectionProviderFactory() {
+				return ijcpf;
+			}
+		};
+		
 		reg = context.registerService(JDBCConnectionProviderFactory.class, 
 				new JDBCConnectionProviderFactoryImpl(), getProperties());
 		
+		msf = new ManagedServiceFactoryImpl(context);
 		factoryReg = context.registerService(ManagedServiceFactory.class, 
-				new ManagedServiceFactoryImpl(context), getMSFProperties());
+				msf, getMSFProperties());
 	}
 
 	@Override
 	public void stop(BundleContext context) throws Exception {
-		reg.unregister();
-		factoryReg.unregister();
+		safeUnregister(reg);
+		safeUnregister(factoryReg);
+		service.close();
+		msf.stop();
 	}
 
+	private void safeUnregister(ServiceRegistration<?> reg) {
+		try {
+			reg.unregister();
+		} catch (IllegalStateException ise) {}
+	}
+	
 	private Dictionary<String, Object> getProperties() {
 		Dictionary<String, Object> props = new Hashtable<>();
 		props.put("osgi.local.enabled", Boolean.TRUE);
diff --git a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
index 28204db..9628031 100644
--- a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
+++ b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
@@ -33,10 +33,10 @@
 import javax.sql.XADataSource;
 
 import org.apache.aries.tx.control.jdbc.common.impl.DriverDataSource;
+import org.apache.aries.tx.control.jdbc.common.impl.InternalJDBCConnectionProviderFactory;
 import org.apache.aries.tx.control.jdbc.xa.connection.impl.XADataSourceMapper;
 import org.osgi.service.jdbc.DataSourceFactory;
 import org.osgi.service.transaction.control.TransactionException;
-import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
 import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,7 +44,7 @@
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 
-public class JDBCConnectionProviderFactoryImpl implements JDBCConnectionProviderFactory {
+public class JDBCConnectionProviderFactoryImpl implements JDBCConnectionProviderFactory, InternalJDBCConnectionProviderFactory {
 
 	private static final Logger LOG = LoggerFactory.getLogger(ManagedServiceFactoryImpl.class);
 	
@@ -91,7 +91,7 @@
 	}
 
 	@Override
-	public JDBCConnectionProvider getProviderFor(DataSource ds, Map<String, Object> resourceProviderProperties) {
+	public JDBCConnectionProviderImpl getProviderFor(DataSource ds, Map<String, Object> resourceProviderProperties) {
 		boolean xaEnabled = toBoolean(resourceProviderProperties, XA_ENLISTMENT_ENABLED, true);
 		boolean localEnabled = toBoolean(resourceProviderProperties, LOCAL_ENLISTMENT_ENABLED, true);
 		
@@ -108,7 +108,7 @@
 	}
 
 	@Override
-	public JDBCConnectionProvider getProviderFor(Driver driver, Properties jdbcProperties, 
+	public JDBCConnectionProviderImpl getProviderFor(Driver driver, Properties jdbcProperties, 
 			Map<String, Object> resourceProviderProperties) {
 		
 		boolean xaEnabled = toBoolean(resourceProviderProperties, XA_ENLISTMENT_ENABLED, false);
@@ -124,7 +124,7 @@
 	}
 
 	@Override
-	public JDBCConnectionProvider getProviderFor(XADataSource ds, Map<String, Object> resourceProviderProperties) {
+	public JDBCConnectionProviderImpl getProviderFor(XADataSource ds, Map<String, Object> resourceProviderProperties) {
 		
 		boolean xaEnabled = toBoolean(resourceProviderProperties, XA_ENLISTMENT_ENABLED, true);
 		boolean localEnabled = toBoolean(resourceProviderProperties, LOCAL_ENLISTMENT_ENABLED, true);
diff --git a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
index 9adc9af..f036f0f 100644
--- a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
+++ b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
@@ -23,16 +23,14 @@
 
 import javax.sql.DataSource;
 
+import org.apache.aries.tx.control.jdbc.common.impl.AbstractJDBCConnectionProvider;
 import org.osgi.service.transaction.control.TransactionControl;
 import org.osgi.service.transaction.control.TransactionException;
-import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
 
-public class JDBCConnectionProviderImpl implements JDBCConnectionProvider {
+public class JDBCConnectionProviderImpl extends AbstractJDBCConnectionProvider {
 
 	private final UUID			uuid	= UUID.randomUUID();
 
-	private final DataSource dataSource;
-	
 	private final boolean xaEnabled;
 	
 	private final boolean localEnabled;
@@ -41,7 +39,7 @@
 	
 	public JDBCConnectionProviderImpl(DataSource dataSource, boolean xaEnabled,
 			boolean localEnabled, String recoveryIdentifier) {
-		this.dataSource = dataSource;
+		super(dataSource);
 		this.xaEnabled = xaEnabled;
 		this.localEnabled = localEnabled;
 		this.recoveryIdentifier = recoveryIdentifier;
diff --git a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
index aa10760..80826c2 100644
--- a/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
+++ b/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
@@ -47,6 +47,7 @@
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.aries.tx.control.jdbc.common.impl.AbstractJDBCConnectionProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -154,6 +155,7 @@
 		private DataSourceFactory activeDsf;
 		private ServiceRegistration<JDBCConnectionProvider> serviceReg;
 		private ServiceRegistration<RecoverableXAResource> recoveryReg;
+		private AbstractJDBCConnectionProvider provider;
 
 		public ManagedJDBCResourceProvider(BundleContext context, String pid, Properties jdbcProperties,
 				Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
@@ -202,9 +204,10 @@
 
 			ServiceRegistration<JDBCConnectionProvider> reg = null;
 			ServiceRegistration<RecoverableXAResource> reg2 = null;
+			JDBCConnectionProviderImpl provider = null;
 			if (setDsf) {
 				try {
-					JDBCConnectionProviderImpl provider = new JDBCConnectionProviderFactoryImpl().getProviderFor(service,
+					provider = new JDBCConnectionProviderFactoryImpl().getProviderFor(service,
 							jdbcProperties, providerProperties);
 					String recoveryId = (String) providerProperties.get(OSGI_RECOVERY_IDENTIFIER);
 					if(recoveryId !=null) {
@@ -223,20 +226,24 @@
 
 					ServiceRegistration<JDBCConnectionProvider> oldReg;
 					ServiceRegistration<RecoverableXAResource> oldReg2;
-					
+					AbstractJDBCConnectionProvider oldProvider;
 					synchronized (this) {
 						if(activeDsf == service) {
 							oldReg = serviceReg;
 							serviceReg = reg;
 							oldReg2 = recoveryReg;
 							recoveryReg = reg2;
+							oldProvider = this.provider;
+							this.provider = provider;
 						} else {
 							oldReg = reg;
 							oldReg2 = reg2;
+							oldProvider = provider;
 						}
 					}
 					safeUnregister(oldReg);
 					safeUnregister(oldReg2);
+					safeClose(oldProvider);
 				} catch (Exception e) {
 					LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
 					
@@ -247,6 +254,7 @@
 					}
 					safeUnregister(reg);
 					safeUnregister(reg2);
+					safeClose(provider);
 				}
 			}
 			return service;
@@ -262,6 +270,16 @@
 			}
 		}
 			
+		private void safeClose(AbstractJDBCConnectionProvider oldProvider) {
+			if(oldProvider != null) {
+				try {
+					oldProvider.close();
+				} catch (Exception e) {
+					LOG.debug("An exception occurred when closing a provider for {}", pid, e);
+				}
+			}
+		}
+
 		private Dictionary<String, ?> getServiceProperties() {
 			Hashtable<String, Object> props = new Hashtable<>();
 			providerProperties.keySet().stream()
@@ -280,18 +298,22 @@
 			boolean dsfLeft;
 			ServiceRegistration<JDBCConnectionProvider> oldReg = null;
 			ServiceRegistration<RecoverableXAResource> oldReg2 = null;
+			AbstractJDBCConnectionProvider oldProvider = null;
 			synchronized (this) {
 				dsfLeft = activeDsf == service;
 				if (dsfLeft) {
 					activeDsf = null;
 					oldReg = serviceReg;
 					oldReg2 = recoveryReg;
+					oldProvider = provider;
 					serviceReg = null;
 					recoveryReg = null;
+					provider = null;
 				}
 			}
 			safeUnregister(oldReg);
 			safeUnregister(oldReg2);
+			safeClose(oldProvider);
 
 			if (dsfLeft) {
 				DataSourceFactory newDSF = dsfTracker.getService();
diff --git a/tx-control/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java b/tx-control/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java
index 00b89df..7149969 100644
--- a/tx-control/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java
+++ b/tx-control/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java
@@ -26,6 +26,7 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.osgi.service.transaction.control.ScopedWorkException;
 import org.osgi.service.transaction.control.TransactionBuilder;
@@ -46,7 +47,12 @@
 			return this;
 		}
 
-		private void checkExceptions() {
+		private void checkValid() {
+			
+			if(closed.get()) {
+				throw new TransactionException("The transaction control service is closed");
+			}
+			
 			List<Class<? extends Throwable>> duplicates = rollbackFor.stream()
 					.filter(noRollbackFor::contains)
 					.collect(toList());
@@ -59,7 +65,7 @@
 		@Override
 		public <T> T required(Callable<T> work)
 				throws TransactionException, TransactionRolledBackException {
-			checkExceptions();
+			checkValid();
 			
 			boolean endTransaction = false;
 
@@ -85,7 +91,7 @@
 		@Override
 		public <T> T requiresNew(Callable<T> work)
 				throws TransactionException, TransactionRolledBackException {
-			checkExceptions();
+			checkValid();
 			
 			AbstractTransactionContextImpl existingTran = existingTx.get();
 			try {
@@ -100,7 +106,7 @@
 
 		@Override
 		public <T> T supports(Callable<T> work) throws TransactionException {
-			checkExceptions();
+			checkValid();
 			
 			boolean endTransaction = false;
 
@@ -124,7 +130,7 @@
 		@Override
 		public <T> T notSupported(Callable<T> work)
 				throws TransactionException {
-			checkExceptions();
+			checkValid();
 			
 			boolean endTransaction = false;
 
@@ -219,6 +225,8 @@
 	}
 
 	private final ThreadLocal<AbstractTransactionContextImpl> existingTx = new ThreadLocal<>();
+	
+	private final AtomicBoolean closed = new AtomicBoolean();
 
 	protected abstract AbstractTransactionContextImpl startTransaction(boolean readOnly);
 
@@ -291,4 +299,7 @@
 
 	}
 
+	public void close() {
+		closed.set(true);
+	}
 }
diff --git a/tx-control/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/Activator.java b/tx-control/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/Activator.java
index 02ee5b8..9492c68 100644
--- a/tx-control/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/Activator.java
+++ b/tx-control/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/Activator.java
@@ -24,24 +24,35 @@
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.transaction.control.TransactionControl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class Activator implements BundleActivator {
 
-private static final Logger logger = LoggerFactory.getLogger(Activator.class);
+	private static final Logger logger = LoggerFactory.getLogger(Activator.class);
+	private TransactionControlImpl service;
+	private ServiceRegistration<TransactionControl> reg;
 	
 	@Override
 	public void start(BundleContext context) throws Exception {
 		Dictionary<String, Object> properties = getProperties();
 		logger.info("Registering a new Local TransactionControl service with properties {}", properties);
-		context.registerService(TransactionControl.class, 
-				new TransactionControlImpl(), properties);
+		service = new TransactionControlImpl();
+		reg = context.registerService(TransactionControl.class, 
+				service, properties);
 	}
 
 	@Override
-	public void stop(BundleContext context) throws Exception { }
+	public void stop(BundleContext context) throws Exception {
+		if(reg != null) {
+			try {
+				reg.unregister();
+			} catch (IllegalStateException ise) { }
+		}
+		service.close();
+	}
 
 	private Dictionary<String, Object> getProperties() {
 		Dictionary<String, Object> props = new Hashtable<>();
diff --git a/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/Activator.java b/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/Activator.java
index 25d1274..e66ba4f 100644
--- a/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/Activator.java
+++ b/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/Activator.java
@@ -138,7 +138,7 @@
 									safeUnregister(newReg);
 								} 
 								if (impl != null) {
-									impl.destroy();
+									impl.close();
 								}
 							}
 							boolean cleanUp = true;
@@ -168,7 +168,7 @@
 			TransactionControlImpl toClose) {
 		safeUnregister(toUnregister);
 		if(toClose != null) {
-			toClose.destroy();
+			toClose.close();
 		}
 	}
 	
diff --git a/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java b/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
index c4db2ec..a5f62da 100644
--- a/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
+++ b/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
@@ -140,7 +140,7 @@
 				recoverableResources = null;
 			}
 		} catch (Exception e) {
-			destroy();
+			close();
 			throw e;
 		}
 	}
@@ -200,7 +200,9 @@
 		return o instanceof Integer ? (Integer) o : Integer.valueOf(o.toString());
 	}
 	
-	public void destroy() {
+	@Override
+	public void close() {
+		super.close();
 		if(recoverableResources != null) {
 			recoverableResources.close();
 		}
diff --git a/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLogTest.java b/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLogTest.java
index b1487f8..6de4bae 100644
--- a/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLogTest.java
+++ b/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLogTest.java
@@ -107,7 +107,7 @@
 	
 	@After
 	public void destroy() {
-		txControl.destroy();
+		txControl.close();
 		try (Connection conn = dataSource.getConnection()) {
 			conn.createStatement().execute("shutdown immediately");
 		} catch (SQLException e) {