ARIES-1616 Further refactoring to remove duplicate code in the JPA resource providers

git-svn-id: https://svn.apache.org/repos/asf/aries/trunk/tx-control@1764111 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java b/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractJPAManagedServiceFactory.java
similarity index 80%
rename from tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java
rename to tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractJPAManagedServiceFactory.java
index 7e7d3e2..76c32f7 100644
--- a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java
+++ b/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractJPAManagedServiceFactory.java
@@ -16,13 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.aries.tx.control.jpa.local.impl;
+package org.apache.aries.tx.control.jpa.common.impl;
 
 import static java.lang.Integer.MAX_VALUE;
 import static java.util.Arrays.asList;
 import static java.util.function.Function.identity;
 import static java.util.stream.Collectors.toMap;
-import static javax.persistence.spi.PersistenceUnitTransactionType.RESOURCE_LOCAL;
 import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATABASE_NAME;
 import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATASOURCE_NAME;
 import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DESCRIPTION;
@@ -42,6 +41,8 @@
 import java.util.Map;
 import java.util.Properties;
 
+import javax.persistence.spi.PersistenceUnitTransactionType;
+
 import org.apache.aries.tx.control.resource.common.impl.ConfigurationDefinedResourceFactory;
 import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
 import org.osgi.framework.BundleContext;
@@ -50,28 +51,23 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ManagedServiceFactoryImpl extends ConfigurationDefinedResourceFactory {
+public abstract class AbstractJPAManagedServiceFactory extends ConfigurationDefinedResourceFactory {
 
-	static final Logger LOG = LoggerFactory.getLogger(ManagedServiceFactoryImpl.class);
+	private static final Logger LOG = LoggerFactory.getLogger(AbstractJPAManagedServiceFactory.class);
 	
-	static final String DSF_TARGET_FILTER = "aries.dsf.target.filter";
-	static final String EMF_BUILDER_TARGET_FILTER = "aries.emf.builder.target.filter";
-	static final String JDBC_PROP_NAMES = "aries.jdbc.property.names";
-	static final List<String> JDBC_PROPERTIES = asList(JDBC_DATABASE_NAME, JDBC_DATASOURCE_NAME,
+	public static final String DSF_TARGET_FILTER = "aries.dsf.target.filter";
+	public static final String EMF_BUILDER_TARGET_FILTER = "aries.emf.builder.target.filter";
+	public static final String JDBC_PROP_NAMES = "aries.jdbc.property.names";
+	public static final List<String> JDBC_PROPERTIES = asList(JDBC_DATABASE_NAME, JDBC_DATASOURCE_NAME,
 			JDBC_DESCRIPTION, JDBC_NETWORK_PROTOCOL, JDBC_PASSWORD, JDBC_PORT_NUMBER, JDBC_ROLE_NAME, JDBC_SERVER_NAME,
 			JDBC_URL, JDBC_USER);
-	static final String JPA_PROP_NAMES = "aries.jpa.property.names";
+	public static final String JPA_PROP_NAMES = "aries.jpa.property.names";
 
-	public ManagedServiceFactoryImpl(BundleContext context) {
+	public AbstractJPAManagedServiceFactory(BundleContext context) {
 		super(context);
 	}
 
 	@Override
-	public String getName() {
-		return "Aries JPAEntityManagerProvider (Local only) service";
-	}
-
-	@Override
 	protected LifecycleAware getConfigurationDrivenResource(BundleContext context, String pid,
 			Map<String, Object> properties) throws Exception {
 
@@ -82,13 +78,13 @@
 			LifecycleAware worker;
 			if(properties.containsKey(OSGI_JDBC_DRIVER_CLASS) ||
 					properties.containsKey(DSF_TARGET_FILTER)) {
-				worker = new ManagedJPADataSourceSetup(context, pid, jdbcProps, jpaProps, properties);
+				worker = dataSourceTracking(context, pid, properties, jdbcProps, jpaProps);
 			} else {
 				if(!jdbcProps.isEmpty()) {
 					LOG.warn("The configuration {} contains raw JDBC configuration, but no osgi.jdbc.driver.class or aries.dsf.target.filter properties. No DataSourceFactory will be used byt this bundle, so the JPA provider must be able to directly create the datasource, and these configuration properties will likely be ignored. {}",
 								pid, jdbcProps.stringPropertyNames());
 				}
-				worker = new ManagedJPAEMFLocator(context, pid, jpaProps, properties, null);
+				worker = emfTracking(context, pid, properties, jpaProps);
 			}
 			return worker;
 		} catch (InvalidSyntaxException e) {
@@ -97,6 +93,13 @@
 		}
 	}
 
+	protected abstract LifecycleAware dataSourceTracking(BundleContext context, String pid,
+			Map<String, Object> properties, Properties jdbcProps, Map<String, Object> jpaProps)
+			throws InvalidSyntaxException, ConfigurationException;
+
+	protected abstract LifecycleAware emfTracking(BundleContext context, String pid, Map<String, Object> properties,
+			Map<String, Object> jpaProps) throws InvalidSyntaxException, ConfigurationException;
+
 	@SuppressWarnings("unchecked")
 	private Properties getJdbcProps(String pid, Map<String, Object> properties) throws ConfigurationException {
 
@@ -143,11 +146,13 @@
 			.filter(propnames::contains)
 			.collect(toMap(identity(), properties::get));
 		
-		result.putIfAbsent("javax.persistence.transactionType", RESOURCE_LOCAL.name());
+		result.putIfAbsent("javax.persistence.transactionType", getTransactionType().name());
 		
 		return result;
 	}
 
+	protected abstract PersistenceUnitTransactionType getTransactionType();
+	
 	private static class AllCollection implements Collection<String> {
 
 		@Override
diff --git a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedJPADataSourceSetup.java b/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
similarity index 69%
rename from tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedJPADataSourceSetup.java
rename to tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
index c23f006..b1891dc 100644
--- a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedJPADataSourceSetup.java
+++ b/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
@@ -16,8 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.aries.tx.control.jpa.xa.impl;
+package org.apache.aries.tx.control.jpa.common.impl;
 
+import static java.util.Collections.unmodifiableMap;
 import static java.util.Optional.ofNullable;
 import static java.util.concurrent.TimeUnit.HOURS;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -29,9 +30,7 @@
 import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.IDLE_TIMEOUT;
 import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MAX_CONNECTIONS;
 import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MIN_CONNECTIONS;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.USE_DRIVER;
 
-import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -41,24 +40,24 @@
 
 import javax.sql.DataSource;
 
-import org.apache.aries.tx.control.jdbc.xa.connection.impl.XADataSourceMapper;
 import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
 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.jdbc.DataSourceFactory;
-import org.osgi.service.transaction.control.TransactionException;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 
-public class ManagedJPADataSourceSetup implements LifecycleAware,
-		ServiceTrackerCustomizer<DataSourceFactory, ManagedJPAEMFLocator> {
+public abstract class AbstractManagedJPADataSourceSetup implements LifecycleAware,
+		ServiceTrackerCustomizer<DataSourceFactory, AbstractManagedJPAEMFLocator> {
 
-	private static final String JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE = "javax.persistence.nonJtaDataSource";
+	private static final Logger LOG = LoggerFactory.getLogger(AbstractManagedJPADataSourceSetup.class);
 	
 	private final BundleContext context;
 	private final String pid;
@@ -66,10 +65,10 @@
 	private final Map<String, Object> baseJPAProperties;
 	private final Map<String, Object> providerProperties;
 	
-	private final ServiceTracker<DataSourceFactory, ManagedJPAEMFLocator> dsfTracker;
+	private final ServiceTracker<DataSourceFactory, AbstractManagedJPAEMFLocator> dsfTracker;
 	private final AtomicReference<ServiceReference<DataSourceFactory>> activeDsf = new AtomicReference<>();
 
-	public ManagedJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties,
+	public AbstractManagedJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties,
 			Map<String, Object> baseJPAProperties, Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
 		this.context = context;
 		this.pid = pid;
@@ -77,11 +76,11 @@
 		this.baseJPAProperties = baseJPAProperties;
 		this.providerProperties = providerProperties;
 
-		String targetFilter = (String) providerProperties.get(ManagedServiceFactoryImpl.DSF_TARGET_FILTER);
+		String targetFilter = (String) providerProperties.get(AbstractJPAManagedServiceFactory.DSF_TARGET_FILTER);
 		if (targetFilter == null) {
 			String driver = (String) providerProperties.get(OSGI_JDBC_DRIVER_CLASS);
 			if (driver == null) {
-				ManagedServiceFactoryImpl.LOG.error("The configuration {} must specify a target filter or a JDBC driver class", pid);
+				LOG.error("The configuration {} must specify a target filter or a JDBC driver class", pid);
 				throw new ConfigurationException(OSGI_JDBC_DRIVER_CLASS,
 						"The configuration must specify either a target filter or a JDBC driver class");
 			}
@@ -102,20 +101,17 @@
 	}
 
 	@Override
-	public ManagedJPAEMFLocator addingService(ServiceReference<DataSourceFactory> reference) {
+	public AbstractManagedJPAEMFLocator addingService(ServiceReference<DataSourceFactory> reference) {
 		DataSourceFactory service = context.getService(reference);
-		ManagedJPAEMFLocator toReturn;
+		AbstractManagedJPAEMFLocator toReturn;
 		try {
-			toReturn = new ManagedJPAEMFLocator(context, pid, 
-					getJPAProperties(service), providerProperties, () -> {
-						Object o = providerProperties.get(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE);
-						if (o instanceof HikariDataSource) {
-							((HikariDataSource)o).close();
-						}
-					});
+			Map<String, Object> jpaProps = decorateJPAProperties(service, 
+					unmodifiableMap(providerProperties), (Properties) jdbcProperties.clone(), 
+					new HashMap<>(baseJPAProperties));
+			toReturn = getManagedJPAEMFLocator(context, pid, jpaProps, providerProperties, 
+					() -> cleanupOnClose(jpaProps));
 		} catch (Exception e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
+			LOG.error("An error occured creating the Resource provider for pid {}", pid, e);
 			return null;
 		}
 		updateService(reference, toReturn);
@@ -123,7 +119,16 @@
 		return toReturn;
 	}
 
-	private void updateService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator locator) {
+	protected abstract Map<String, Object> decorateJPAProperties(DataSourceFactory dsf, 
+			Map<String, Object> providerProperties, Properties jdbcProperties,
+			Map<String, Object> jpaProperties) throws Exception;
+	
+	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;
+
+	private void updateService(ServiceReference<DataSourceFactory> reference, AbstractManagedJPAEMFLocator locator) {
 		boolean setDsf;
 		synchronized (this) {
 			setDsf = activeDsf.compareAndSet(null, reference);
@@ -133,51 +138,30 @@
 				locator.start();
 			}
 		} catch (Exception e) {
-			ManagedServiceFactoryImpl.LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
+			LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
 			activeDsf.compareAndSet(reference, null);
 			throw new IllegalStateException("An error occurred when creating the connection provider", e);
 		}
 	}
 
-	private Map<String, Object> getJPAProperties(DataSourceFactory dsf) {
-		Map<String, Object> props = new HashMap<>(baseJPAProperties);
-		
-		DataSource unpooled;
-		try {
-			if (toBoolean(providerProperties, USE_DRIVER, false)) {
-				throw new TransactionException("The Database must use an XA connection");
-			} else {
-				unpooled = new XADataSourceMapper(dsf.createXADataSource(jdbcProperties));
-			}
-		} catch (SQLException sqle) {
-			throw new TransactionException("Unable to create the JDBC resource provider", sqle);
-		}
-
-		DataSource toUse = poolIfNecessary(providerProperties, unpooled);
-		
-		props.put("javax.persistence.jtaDataSource", toUse);
-		
-		return props;
-	}
-	
 	@Override
-	public void modifiedService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator service) {
+	public void modifiedService(ServiceReference<DataSourceFactory> reference, AbstractManagedJPAEMFLocator service) {
 	}
 
 	@Override
-	public void removedService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator service) {
+	public void removedService(ServiceReference<DataSourceFactory> reference, AbstractManagedJPAEMFLocator service) {
 		service.stop();
 
 		if (activeDsf.compareAndSet(reference, null)) {
-			Map<ServiceReference<DataSourceFactory>,ManagedJPAEMFLocator> tracked = dsfTracker.getTracked();
+			Map<ServiceReference<DataSourceFactory>,AbstractManagedJPAEMFLocator> tracked = dsfTracker.getTracked();
 			if (!tracked.isEmpty()) {
-				Entry<ServiceReference<DataSourceFactory>, ManagedJPAEMFLocator> e = tracked.entrySet().iterator().next();
+				Entry<ServiceReference<DataSourceFactory>, AbstractManagedJPAEMFLocator> e = tracked.entrySet().iterator().next();
 				updateService(e.getKey(), e.getValue());
 			}
 		}
 	}
 	
-	private DataSource poolIfNecessary(Map<String, Object> resourceProviderProperties, DataSource unpooled) {
+	protected DataSource poolIfNecessary(Map<String, Object> resourceProviderProperties, DataSource unpooled) {
 		DataSource toUse;
 
 		if (toBoolean(resourceProviderProperties, CONNECTION_POOLING_ENABLED, true)) {
@@ -201,7 +185,7 @@
 		return toUse;
 	}
 
-	private boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
+	protected boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
 		Object o =  ofNullable(props)
 			.map(m -> m.get(key))
 			.orElse(defaultValue);
@@ -215,7 +199,7 @@
 		}
 	}
 
-	private int toInt(Map<String, Object> props, String key, int defaultValue) {
+	protected int toInt(Map<String, Object> props, String key, int defaultValue) {
 		
 		Object o =  ofNullable(props)
 				.map(m -> m.get(key))
diff --git a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java b/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPAEMFLocator.java
similarity index 74%
rename from tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java
rename to tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPAEMFLocator.java
index 8c11321..557d39a 100644
--- a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java
+++ b/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPAEMFLocator.java
@@ -16,9 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.aries.tx.control.jpa.local.impl;
+package org.apache.aries.tx.control.jpa.common.impl;
 
-import static org.apache.aries.tx.control.jpa.local.impl.ManagedServiceFactoryImpl.EMF_BUILDER_TARGET_FILTER;
+import static org.apache.aries.tx.control.jpa.common.impl.AbstractJPAManagedServiceFactory.EMF_BUILDER_TARGET_FILTER;
 import static org.osgi.framework.Constants.OBJECTCLASS;
 import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PASSWORD;
 import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_NAME;
@@ -39,10 +39,14 @@
 import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class ManagedJPAEMFLocator implements LifecycleAware,
+public abstract class AbstractManagedJPAEMFLocator implements LifecycleAware,
 	ServiceTrackerCustomizer<EntityManagerFactoryBuilder, EntityManagerFactoryBuilder> {
 
+	private static final Logger LOG = LoggerFactory.getLogger(AbstractJPAEntityManagerProvider.class);
+	
 	private final BundleContext context;
 	private final String pid;
 	private final Map<String, Object> jpaProperties;
@@ -55,7 +59,7 @@
 	
 	private final AtomicReference<ServiceRegistration<JPAEntityManagerProvider>> serviceReg = new AtomicReference<>();
 
-	public ManagedJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
+	public AbstractManagedJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
 			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
 		this.context = context;
 		this.pid = pid;
@@ -65,7 +69,7 @@
 
 		String unitName = (String) providerProperties.get(JPA_UNIT_NAME);
 		if (unitName == null) {
-			ManagedServiceFactoryImpl.LOG.error("The configuration {} must specify a persistence unit name", pid);
+			LOG.error("The configuration {} must specify a persistence unit name", pid);
 			throw new ConfigurationException(JPA_UNIT_NAME,
 					"The configuration must specify a persistence unit name");
 		}
@@ -92,11 +96,11 @@
 	public EntityManagerFactoryBuilder addingService(ServiceReference<EntityManagerFactoryBuilder> reference) {
 		EntityManagerFactoryBuilder service = context.getService(reference);
 
-		updateService(service);
+		updateService(reference, service);
 		return service;
 	}
 
-	private void updateService(EntityManagerFactoryBuilder service) {
+	private void updateService(ServiceReference<EntityManagerFactoryBuilder> reference, EntityManagerFactoryBuilder service) {
 		boolean setEMFB;
 		synchronized (this) {
 			setEMFB = activeEMFB.compareAndSet(null, service);
@@ -105,8 +109,7 @@
 		if (setEMFB) {
 			AbstractJPAEntityManagerProvider provider = null;
 			try {
-				provider = new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
-						jpaProperties, providerProperties, onClose);
+				provider = getResourceProvider(context, service, reference, jpaProperties, providerProperties, onClose);
 				providerObject.set(provider);
 				ServiceRegistration<JPAEntityManagerProvider> reg = context
 						.registerService(JPAEntityManagerProvider.class, provider, getServiceProperties());
@@ -114,7 +117,7 @@
 					throw new IllegalStateException("Unable to set the JDBC connection provider registration");
 				}
 			} catch (Exception e) {
-				ManagedServiceFactoryImpl.LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
+				LOG.error("An error occurred when creating the resource provider for {}.", pid, e);
 				activeEMFB.compareAndSet(service, null);
 				if(provider != null) {
 					provider.close();
@@ -124,6 +127,10 @@
 		}
 	}
 
+	protected abstract AbstractJPAEntityManagerProvider getResourceProvider(BundleContext context, 
+			EntityManagerFactoryBuilder service, ServiceReference<EntityManagerFactoryBuilder> reference, 
+			Map<String, Object> jpaProperties, Map<String, Object> providerProperties, Runnable onClose);
+
 	private Dictionary<String, ?> getServiceProperties() {
 		Hashtable<String, Object> props = new Hashtable<>();
 		providerProperties.keySet().stream().filter(s -> !JDBC_PASSWORD.equals(s))
@@ -152,7 +159,7 @@
 			try {
 				oldReg.unregister();
 			} catch (IllegalStateException ise) {
-				ManagedServiceFactoryImpl.LOG.debug("An exception occurred when unregistering a service for {}", pid);
+				LOG.debug("An exception occurred when unregistering a service for {}", pid);
 			}
 		}
 		
@@ -160,20 +167,24 @@
 			try {
 				toClose.close();
 			} catch (Exception e) {
-				ManagedServiceFactoryImpl.LOG.debug("An Exception occured when closing the Resource provider for {}", pid, e);
+				LOG.debug("An Exception occured when closing the Resource provider for {}", pid, e);
 			}
 		}
 		
 		try {
 			context.ungetService(reference);
 		} catch (IllegalStateException ise) {
-			ManagedServiceFactoryImpl.LOG.debug("An exception occurred when ungetting the service for {}", reference);
+			LOG.debug("An exception occurred when ungetting the service for {}", reference);
 		}
 
 		if (emfbLeft) {
-			EntityManagerFactoryBuilder newEMFBuilder = emfBuilderTracker.getService();
-			if (newEMFBuilder != null) {
-				updateService(newEMFBuilder);
+			ServiceReference<EntityManagerFactoryBuilder> newEMFBuilderRef = emfBuilderTracker
+					.getServiceReference();
+			if (newEMFBuilderRef != null) {
+				EntityManagerFactoryBuilder newEMFBuilder = emfBuilderTracker.getService(newEMFBuilderRef);
+				if(newEMFBuilder != null) {
+					updateService(newEMFBuilderRef, newEMFBuilder);
+				}
 			}
 		}
 	}
diff --git a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java
index 7ed5633..e477fac 100644
--- a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java
+++ b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java
@@ -55,7 +55,7 @@
 
 	@Override
 	protected ConfigurationDefinedResourceFactory getConfigurationDefinedResourceFactory(BundleContext context) {
-		return new ManagedServiceFactoryImpl(context);
+		return new LocalJPAManagedServiceFactory(context);
 	}
 
 	@Override
diff --git a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
new file mode 100644
index 0000000..94d8956
--- /dev/null
+++ b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
@@ -0,0 +1,86 @@
+/*
+ * 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.jpa.local.impl;
+
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_URL;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.USE_DRIVER;
+
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPADataSourceSetup;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.osgi.service.transaction.control.TransactionException;
+
+import com.zaxxer.hikari.HikariDataSource;
+
+public class LocalJPADataSourceSetup extends AbstractManagedJPADataSourceSetup {
+
+	private static final String JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE = "javax.persistence.nonJtaDataSource";
+
+	public LocalJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties,
+			Map<String, Object> baseJPAProperties, Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
+		super(context, pid, jdbcProperties, baseJPAProperties, providerProperties);
+	}
+
+	@Override
+	protected Map<String, Object> decorateJPAProperties(DataSourceFactory dsf, 
+			Map<String, Object> providerProperties, Properties jdbcProperties, 
+			Map<String, Object> jpaProperties) {
+		DataSource unpooled;
+		try {
+			if (toBoolean(providerProperties, USE_DRIVER, false)) {
+				unpooled = new DriverDataSource(dsf.createDriver(null), jdbcProperties.getProperty(JDBC_URL),
+						jdbcProperties);
+			} else {
+				unpooled = dsf.createDataSource(jdbcProperties);
+			}
+		} catch (SQLException sqle) {
+			throw new TransactionException("Unable to create the JDBC resource provider", sqle);
+		}
+
+		DataSource toUse = poolIfNecessary(providerProperties, unpooled);
+		
+		jpaProperties.put(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE, toUse);
+		
+		return jpaProperties;
+	}
+
+	@Override
+	protected void cleanupOnClose(Map<String, Object> jpaProperties) {
+		Object o = jpaProperties.get(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE);
+		if (o instanceof HikariDataSource) {
+			((HikariDataSource)o).close();
+		}
+	}
+
+	@Override
+	protected AbstractManagedJPAEMFLocator getManagedJPAEMFLocator(BundleContext context, String pid,
+			Map<String, Object> jpaProps, Map<String, Object> providerProperties, Runnable onClose) 
+					throws InvalidSyntaxException, ConfigurationException {
+		return new LocalJPAEMFLocator(context, pid, jpaProps, providerProperties, onClose);
+	}
+}
\ No newline at end of file
diff --git a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocator.java b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocator.java
new file mode 100644
index 0000000..ad2364b
--- /dev/null
+++ b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAEMFLocator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.jpa.local.impl;
+
+import java.util.Map;
+
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
+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;
+
+public class LocalJPAEMFLocator extends AbstractManagedJPAEMFLocator {
+
+	public LocalJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
+			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
+		super(context, pid, jpaProperties, providerProperties, onClose);
+	}
+
+	@Override
+	protected AbstractJPAEntityManagerProvider getResourceProvider(BundleContext context,
+			EntityManagerFactoryBuilder service, ServiceReference<EntityManagerFactoryBuilder> reference,
+			Map<String, Object> jpaProperties, Map<String, Object> providerProperties, Runnable onClose) {
+		return new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
+				jpaProperties, providerProperties, onClose);
+	}
+}
\ No newline at end of file
diff --git a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAManagedServiceFactory.java b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAManagedServiceFactory.java
new file mode 100644
index 0000000..046e6dd
--- /dev/null
+++ b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPAManagedServiceFactory.java
@@ -0,0 +1,61 @@
+/*
+ * 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.jpa.local.impl;
+
+import java.util.Map;
+import java.util.Properties;
+
+import javax.persistence.spi.PersistenceUnitTransactionType;
+
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAManagedServiceFactory;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPADataSourceSetup;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
+import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.ConfigurationException;
+
+public class LocalJPAManagedServiceFactory extends AbstractJPAManagedServiceFactory {
+
+	public LocalJPAManagedServiceFactory(BundleContext context) {
+		super(context);
+	}
+
+	@Override
+	public String getName() {
+		return "Aries JPAEntityManagerProvider (Local only) service";
+	}
+
+	@Override
+	protected AbstractManagedJPADataSourceSetup dataSourceTracking(BundleContext context, String pid, Map<String, Object> properties,
+			Properties jdbcProps, Map<String, Object> jpaProps) throws InvalidSyntaxException, ConfigurationException {
+		return new LocalJPADataSourceSetup(context, pid, jdbcProps, jpaProps, properties);
+	}
+
+	@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);
+	}
+
+	@Override
+	protected PersistenceUnitTransactionType getTransactionType() {
+		return PersistenceUnitTransactionType.RESOURCE_LOCAL;
+	}
+}
diff --git a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java b/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java
deleted file mode 100644
index 870e5b4..0000000
--- a/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * 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.jpa.local.impl;
-
-import static java.util.Optional.ofNullable;
-import static java.util.concurrent.TimeUnit.HOURS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.osgi.framework.Constants.OBJECTCLASS;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_URL;
-import static org.osgi.service.jdbc.DataSourceFactory.OSGI_JDBC_DRIVER_CLASS;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_LIFETIME;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_POOLING_ENABLED;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_TIMEOUT;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.IDLE_TIMEOUT;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MAX_CONNECTIONS;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MIN_CONNECTIONS;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.USE_DRIVER;
-
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.sql.DataSource;
-
-import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
-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.jdbc.DataSourceFactory;
-import org.osgi.service.transaction.control.TransactionException;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-
-import com.zaxxer.hikari.HikariConfig;
-import com.zaxxer.hikari.HikariDataSource;
-
-public class ManagedJPADataSourceSetup implements LifecycleAware,
-		ServiceTrackerCustomizer<DataSourceFactory, ManagedJPAEMFLocator> {
-
-	private static final String JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE = "javax.persistence.nonJtaDataSource";
-	
-	private final BundleContext context;
-	private final String pid;
-	private final Properties jdbcProperties;
-	private final Map<String, Object> baseJPAProperties;
-	private final Map<String, Object> providerProperties;
-	
-	private final ServiceTracker<DataSourceFactory, ManagedJPAEMFLocator> dsfTracker;
-	private final AtomicReference<ServiceReference<DataSourceFactory>> activeDsf = new AtomicReference<>();
-
-	public ManagedJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties,
-			Map<String, Object> baseJPAProperties, Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
-		this.context = context;
-		this.pid = pid;
-		this.jdbcProperties = jdbcProperties;
-		this.baseJPAProperties = baseJPAProperties;
-		this.providerProperties = providerProperties;
-
-		String targetFilter = (String) providerProperties.get(ManagedServiceFactoryImpl.DSF_TARGET_FILTER);
-		if (targetFilter == null) {
-			String driver = (String) providerProperties.get(OSGI_JDBC_DRIVER_CLASS);
-			if (driver == null) {
-				ManagedServiceFactoryImpl.LOG.error("The configuration {} must specify a target filter or a JDBC driver class", pid);
-				throw new ConfigurationException(OSGI_JDBC_DRIVER_CLASS,
-						"The configuration must specify either a target filter or a JDBC driver class");
-			}
-			targetFilter = "(" + OSGI_JDBC_DRIVER_CLASS + "=" + driver + ")";
-		}
-
-		targetFilter = "(&(" + OBJECTCLASS + "=" + DataSourceFactory.class.getName() + ")" + targetFilter + ")";
-
-		this.dsfTracker = new ServiceTracker<>(context, context.createFilter(targetFilter), this);
-	}
-
-	public void start() {
-		dsfTracker.open();
-	}
-
-	public void stop() {
-		dsfTracker.close();
-	}
-
-	@Override
-	public ManagedJPAEMFLocator addingService(ServiceReference<DataSourceFactory> reference) {
-		DataSourceFactory service = context.getService(reference);
-		ManagedJPAEMFLocator toReturn;
-		try {
-			toReturn = new ManagedJPAEMFLocator(context, pid, 
-					getJPAProperties(service), providerProperties, () -> {
-						Object o = providerProperties.get(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE);
-						if (o instanceof HikariDataSource) {
-							((HikariDataSource)o).close();
-						}
-					});
-		} catch (Exception e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			return null;
-		}
-		updateService(reference, toReturn);
-		
-		return toReturn;
-	}
-
-	private void updateService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator locator) {
-		boolean setDsf;
-		synchronized (this) {
-			setDsf = activeDsf.compareAndSet(null, reference);
-		}
-		try {
-			if (setDsf) {
-				locator.start();
-			}
-		} catch (Exception e) {
-			ManagedServiceFactoryImpl.LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
-			activeDsf.compareAndSet(reference, null);
-			throw new IllegalStateException("An error occurred when creating the connection provider", e);
-		}
-	}
-
-	private Map<String, Object> getJPAProperties(DataSourceFactory dsf) {
-		Map<String, Object> props = new HashMap<>(baseJPAProperties);
-		
-		DataSource unpooled;
-		try {
-			if (toBoolean(providerProperties, USE_DRIVER, false)) {
-				unpooled = new DriverDataSource(dsf.createDriver(null), jdbcProperties.getProperty(JDBC_URL),
-						jdbcProperties);
-			} else {
-				unpooled = dsf.createDataSource(jdbcProperties);
-			}
-		} catch (SQLException sqle) {
-			throw new TransactionException("Unable to create the JDBC resource provider", sqle);
-		}
-
-		DataSource toUse = poolIfNecessary(providerProperties, unpooled);
-		
-		props.put(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE, toUse);
-		
-		return props;
-	}
-	
-	@Override
-	public void modifiedService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator service) {
-	}
-
-	@Override
-	public void removedService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator service) {
-		service.stop();
-
-		if (activeDsf.compareAndSet(reference, null)) {
-			Map<ServiceReference<DataSourceFactory>,ManagedJPAEMFLocator> tracked = dsfTracker.getTracked();
-			if (!tracked.isEmpty()) {
-				Entry<ServiceReference<DataSourceFactory>, ManagedJPAEMFLocator> e = tracked.entrySet().iterator().next();
-				updateService(e.getKey(), e.getValue());
-			}
-		}
-	}
-	
-	private DataSource poolIfNecessary(Map<String, Object> resourceProviderProperties, DataSource unpooled) {
-		DataSource toUse;
-
-		if (toBoolean(resourceProviderProperties, CONNECTION_POOLING_ENABLED, true)) {
-			HikariConfig hcfg = new HikariConfig();
-			hcfg.setDataSource(unpooled);
-
-			// Sizes
-			hcfg.setMaximumPoolSize(toInt(resourceProviderProperties, MAX_CONNECTIONS, 10));
-			hcfg.setMinimumIdle(toInt(resourceProviderProperties, MIN_CONNECTIONS, 10));
-
-			// Timeouts
-			hcfg.setConnectionTimeout(toLong(resourceProviderProperties, CONNECTION_TIMEOUT, SECONDS.toMillis(30)));
-			hcfg.setIdleTimeout(toLong(resourceProviderProperties, IDLE_TIMEOUT, TimeUnit.MINUTES.toMillis(3)));
-			hcfg.setMaxLifetime(toLong(resourceProviderProperties, CONNECTION_LIFETIME, HOURS.toMillis(3)));
-
-			toUse = new HikariDataSource(hcfg);
-
-		} else {
-			toUse = unpooled;
-		}
-		return toUse;
-	}
-
-	private boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
-		Object o =  ofNullable(props)
-			.map(m -> m.get(key))
-			.orElse(defaultValue);
-		
-		if (o instanceof Boolean) {
-			return ((Boolean) o).booleanValue();
-		} else if(o instanceof String) {
-			return Boolean.parseBoolean((String) o);
-		} else {
-			throw new IllegalArgumentException("The property " + key + " cannot be converted to a boolean");
-		}
-	}
-
-	private int toInt(Map<String, Object> props, String key, int defaultValue) {
-		
-		Object o =  ofNullable(props)
-				.map(m -> m.get(key))
-				.orElse(defaultValue);
-		
-		if (o instanceof Number) {
-			return ((Number) o).intValue();
-		} else if(o instanceof String) {
-			return Integer.parseInt((String) o);
-		} else {
-			throw new IllegalArgumentException("The property " + key + " cannot be converted to an int");
-		}
-	}
-
-	private long toLong(Map<String, Object> props, String key, long defaultValue) {
-		
-		Object o =  ofNullable(props)
-				.map(m -> m.get(key))
-				.orElse(defaultValue);
-		
-		if (o instanceof Number) {
-			return ((Number) o).longValue();
-		} else if(o instanceof String) {
-			return Long.parseLong((String) o);
-		} else {
-			throw new IllegalArgumentException("The property " + key + " cannot be converted to a long");
-		}
-	}
-
-}
\ No newline at end of file
diff --git a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java
index a5e6d83..6146843 100644
--- a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java
+++ b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java
@@ -55,7 +55,7 @@
 
 	@Override
 	protected ConfigurationDefinedResourceFactory getConfigurationDefinedResourceFactory(BundleContext context) {
-		return new ManagedServiceFactoryImpl(context);
+		return new XAJPAManagedServiceFactory(context);
 	}
 
 	@Override
diff --git a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedJPAEMFLocator.java b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedJPAEMFLocator.java
deleted file mode 100644
index fdbca33..0000000
--- a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedJPAEMFLocator.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * 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.jpa.xa.impl;
-
-import static org.apache.aries.tx.control.jpa.xa.impl.ManagedServiceFactoryImpl.EMF_BUILDER_TARGET_FILTER;
-import static org.osgi.framework.Constants.OBJECTCLASS;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PASSWORD;
-import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_NAME;
-import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_PROVIDER;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.persistence.spi.PersistenceProvider;
-
-import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
-import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.framework.wiring.BundleWire;
-import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.jpa.EntityManagerFactoryBuilder;
-import org.osgi.service.transaction.control.TransactionControl;
-import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-
-public class ManagedJPAEMFLocator implements LifecycleAware,
-	ServiceTrackerCustomizer<EntityManagerFactoryBuilder, EntityManagerFactoryBuilder> {
-
-	private final BundleContext context;
-	private final String pid;
-	private final Map<String, Object> jpaProperties;
-	private final Map<String, Object> providerProperties;
-	private final Runnable onClose;
-	private final ServiceTracker<EntityManagerFactoryBuilder, EntityManagerFactoryBuilder> emfBuilderTracker;
-
-	private final AtomicReference<EntityManagerFactoryBuilder> activeEMFB = new AtomicReference<>();
-	private final AtomicReference<AbstractJPAEntityManagerProvider> providerObject = new AtomicReference<>();
-	
-	private final AtomicReference<ServiceRegistration<JPAEntityManagerProvider>> serviceReg = new AtomicReference<>();
-
-	public ManagedJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
-			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
-		this.context = context;
-		this.pid = pid;
-		this.jpaProperties = jpaProperties;
-		this.providerProperties = providerProperties;
-		this.onClose = onClose;
-
-		String unitName = (String) providerProperties.get(JPA_UNIT_NAME);
-		if (unitName == null) {
-			ManagedServiceFactoryImpl.LOG.error("The configuration {} must specify a persistence unit name", pid);
-			throw new ConfigurationException(JPA_UNIT_NAME,
-					"The configuration must specify a persistence unit name");
-		}
-		
-		String targetFilter = (String) providerProperties.get(EMF_BUILDER_TARGET_FILTER);
-		if (targetFilter == null) {
-			targetFilter = "(" + JPA_UNIT_NAME + "=" + unitName + ")";
-		}
-
-		targetFilter = "(&(" + OBJECTCLASS + "=" + EntityManagerFactoryBuilder.class.getName() + ")" + targetFilter + ")";
-
-		this.emfBuilderTracker = new ServiceTracker<>(context, context.createFilter(targetFilter), this);
-	}
-
-	public void start() {
-		emfBuilderTracker.open();
-	}
-
-	public void stop() {
-		emfBuilderTracker.close();
-	}
-
-	@Override
-	public EntityManagerFactoryBuilder addingService(ServiceReference<EntityManagerFactoryBuilder> reference) {
-		EntityManagerFactoryBuilder service = context.getService(reference);
-
-		updateService(reference, service);
-		return service;
-	}
-
-	private void updateService(ServiceReference<EntityManagerFactoryBuilder> reference, EntityManagerFactoryBuilder service) {
-		boolean setEMFB;
-		synchronized (this) {
-			setEMFB = activeEMFB.compareAndSet(null, service);
-		}
-
-		if (setEMFB) {
-			AbstractJPAEntityManagerProvider jpaEM = null;
-			try {
-				jpaEM = new DelayedJPAEntityManagerProvider(t -> {
-					
-					Map<String, Object> jpaProps = new HashMap<String, Object>(jpaProperties);
-					Map<String, Object> providerProps = new HashMap<String, Object>(providerProperties);
-					
-					setupTransactionManager(jpaProps, providerProps, t, reference);
-					
-					return new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
-							jpaProps, providerProps, t, onClose);
-				});
-				providerObject.set(jpaEM);
-				ServiceRegistration<JPAEntityManagerProvider> reg = context
-						.registerService(JPAEntityManagerProvider.class, jpaEM, getServiceProperties());
-				if (!serviceReg.compareAndSet(null, reg)) {
-					throw new IllegalStateException("Unable to set the JDBC connection provider registration");
-				}
-			} catch (Exception e) {
-				ManagedServiceFactoryImpl.LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
-				activeEMFB.compareAndSet(service, null);
-				if(jpaEM != null) {
-					jpaEM.close();
-				}
-			}
-		}
-	}
-
-	private void setupTransactionManager(Map<String, Object> props, Map<String, Object> providerProps, 
-			ThreadLocal<TransactionControl> t, ServiceReference<EntityManagerFactoryBuilder> reference) {
-		String provider = (String) reference.getProperty(JPA_UNIT_PROVIDER);
-		
-		ServiceReference<PersistenceProvider> providerRef = getPersistenceProvider(provider);
-		
-		if(providerRef == null) {
-			// TODO log a warning and give up
-			return;
-		}
-
-		Bundle providerBundle = providerRef.getBundle();
-		Bundle txControlProviderBundle = context.getBundle();
-		
-		try {
-			if("org.hibernate.jpa.HibernatePersistenceProvider".equals(provider)) {
-				
-				try{
-					providerBundle.loadClass("org.hibernate.resource.transaction.TransactionCoordinatorBuilder");
-				} catch (Exception e) {
-					BundleWiring wiring = providerBundle.adapt(BundleWiring.class);
-					providerBundle = wiring.getRequiredWires("osgi.wiring.package").stream()
-								.filter(bw -> "org.hibernate".equals(bw.getCapability().getAttributes().get("osgi.wiring.package")))
-								.map(BundleWire::getProviderWiring)
-								.map(BundleWiring::getBundle)
-								.findFirst().get();
-				}
-				
-				ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
-				
-				Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.hibernate.impl.HibernateTxControlPlatform");
-				Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
-					.newInstance(t);
-				
-				props.put("hibernate.transaction.coordinator_class", plugin);
-				
-			} else if("org.apache.openjpa.persistence.PersistenceProviderImpl".equals(provider)) {
-					
-				ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
-					
-				Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.openjpa.impl.OpenJPATxControlPlatform");
-				Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
-						.newInstance(t);
-					
-				props.put("openjpa.ManagedRuntime", plugin);
-					
-			} else if("org.eclipse.persistence.jpa.PersistenceProvider".equals(provider)) {
-				
-				ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
-				
-				Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.eclipse.impl.EclipseTxControlPlatform");
-				
-				pluginClazz.getMethod("setTransactionControl", ThreadLocal.class)
-						.invoke(null, t);
-				
-				props.put("eclipselink.target-server", pluginClazz.getName());
-				props.put("org.apache.aries.jpa.eclipselink.plugin.types", pluginClazz);
-				// This is needed to ensure that sequences can be generated in nested
-				// transactions without blowing up.
-				if(!props.containsKey("eclipselink.jdbc.sequence-connection-pool")) {
-					props.put("eclipselink.jdbc.sequence-connection-pool", "true");
-				}
-				
-			} else {
-				// TODO log a warning and give up
-				return;
-			} 
-		} catch (Exception e) {
-			//TODO log a warning and give up
-			e.printStackTrace();
-		}
-	}
-
-	private ClassLoader getPluginLoader(Bundle providerBundle, Bundle txControlProviderBundle) {
-		return new ClassLoader() {
-
-			ConcurrentMap<String, Class<?>> loaded = new ConcurrentHashMap<>();
-			
-			@Override
-			public Class<?> loadClass(String name) throws ClassNotFoundException {
-				if(name.startsWith("org.apache.aries.tx.control.jpa.xa.hibernate") ||
-					name.startsWith("org.apache.aries.tx.control.jpa.xa.openjpa") ||
-					name.startsWith("org.apache.aries.tx.control.jpa.xa.eclipse")) {
-					
-					Class<?> c = loaded.get(name);
-					
-					if(c != null) {
-						return c;
-					}
-					
-					String resource = name.replace('.', '/') + ".class";
-					
-					try (InputStream is = txControlProviderBundle.getResource(resource).openStream()) {
-						ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
-						byte[] b = new byte[4096];
-						int read;
-						while ((read = is.read(b)) != -1) {
-							baos.write(b, 0, read);
-						}
-						byte[] clazzBytes = baos.toByteArray();
-						c = defineClass(name, clazzBytes, 0, clazzBytes.length, 
-								ManagedJPAEMFLocator.class.getProtectionDomain());
-						loaded.putIfAbsent(name, c);
-						return c;
-					} catch (IOException e) {
-						throw new ClassNotFoundException("Unable to load class " + name, e);
-					}
-				}
-				
-				if(name.startsWith("org.apache.aries.tx.control") ||
-						name.startsWith("org.osgi.service.transaction.control")) {
-					return txControlProviderBundle.loadClass(name);
-				}
-				return providerBundle.loadClass(name);
-			}
-		};
-	}
-
-	private ServiceReference<PersistenceProvider> getPersistenceProvider(String provider) {
-		if(provider == null) {
-			return null;
-		}
-		try {
-			return context.getServiceReferences(PersistenceProvider.class, 
-							"(javax.persistence.provider=" + provider + ")").stream()
-								.findFirst()
-								.orElse(null);
-		} catch (InvalidSyntaxException e) {
-			//TODO log a warning
-			return null;
-		} 
-	}
-
-	private Dictionary<String, ?> getServiceProperties() {
-		Hashtable<String, Object> props = new Hashtable<>();
-		providerProperties.keySet().stream().filter(s -> !JDBC_PASSWORD.equals(s))
-				.forEach(s -> props.put(s, providerProperties.get(s)));
-		return props;
-	}
-
-	@Override
-	public void modifiedService(ServiceReference<EntityManagerFactoryBuilder> reference, EntityManagerFactoryBuilder service) {
-	}
-
-	@Override
-	public void removedService(ServiceReference<EntityManagerFactoryBuilder> reference, EntityManagerFactoryBuilder service) {
-		boolean emfbLeft;
-		ServiceRegistration<JPAEntityManagerProvider> oldReg = null;
-		AbstractJPAEntityManagerProvider toClose = null;
-		synchronized (this) {
-			emfbLeft = activeEMFB.compareAndSet(service, null);
-			if (emfbLeft) {
-				toClose = providerObject.get();
-				oldReg = serviceReg.getAndSet(null);
-			}
-		}
-
-		if (oldReg != null) {
-			try {
-				oldReg.unregister();
-			} catch (IllegalStateException ise) {
-				ManagedServiceFactoryImpl.LOG.debug("An exception occurred when unregistering a service for {}", pid);
-			}
-		}
-		
-		if(toClose != null) {
-			try {
-				toClose.close();
-			} catch (Exception e) {
-				ManagedServiceFactoryImpl.LOG.debug("An Exception occured when closing the Resource provider for {}", pid, e);
-			}
-		}
-		
-		try {
-			context.ungetService(reference);
-		} catch (IllegalStateException ise) {
-			ManagedServiceFactoryImpl.LOG.debug("An exception occurred when ungetting the service for {}", reference);
-		}
-
-		if (emfbLeft) {
-			EntityManagerFactoryBuilder newEMFBuilder = emfBuilderTracker.getService();
-			if (newEMFBuilder != null) {
-				updateService(reference, newEMFBuilder);
-			}
-		}
-	}
-}
\ No newline at end of file
diff --git a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedServiceFactoryImpl.java b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedServiceFactoryImpl.java
deleted file mode 100644
index 33fbe3a..0000000
--- a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ManagedServiceFactoryImpl.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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.jpa.xa.impl;
-
-import static java.lang.Integer.MAX_VALUE;
-import static java.util.Arrays.asList;
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toMap;
-import static javax.persistence.spi.PersistenceUnitTransactionType.JTA;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATABASE_NAME;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATASOURCE_NAME;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DESCRIPTION;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_NETWORK_PROTOCOL;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PASSWORD;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PORT_NUMBER;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_ROLE_NAME;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_SERVER_NAME;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_URL;
-import static org.osgi.service.jdbc.DataSourceFactory.JDBC_USER;
-import static org.osgi.service.jdbc.DataSourceFactory.OSGI_JDBC_DRIVER_CLASS;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import org.apache.aries.tx.control.resource.common.impl.ConfigurationDefinedResourceFactory;
-import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.cm.ConfigurationException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ManagedServiceFactoryImpl extends ConfigurationDefinedResourceFactory {
-
-	static final Logger LOG = LoggerFactory.getLogger(ManagedServiceFactoryImpl.class);
-	
-	static final String DSF_TARGET_FILTER = "aries.dsf.target.filter";
-	static final String EMF_BUILDER_TARGET_FILTER = "aries.emf.builder.target.filter";
-	static final String JDBC_PROP_NAMES = "aries.jdbc.property.names";
-	static final List<String> JDBC_PROPERTIES = asList(JDBC_DATABASE_NAME, JDBC_DATASOURCE_NAME,
-			JDBC_DESCRIPTION, JDBC_NETWORK_PROTOCOL, JDBC_PASSWORD, JDBC_PORT_NUMBER, JDBC_ROLE_NAME, JDBC_SERVER_NAME,
-			JDBC_URL, JDBC_USER);
-	static final String JPA_PROP_NAMES = "aries.jpa.property.names";
-
-	public ManagedServiceFactoryImpl(BundleContext context) {
-		super(context);
-	}
-
-	@Override
-	public String getName() {
-		return "Aries JPAEntityManagerProvider (XA only) service";
-	}
-
-	@Override
-	protected LifecycleAware getConfigurationDrivenResource(BundleContext context, String pid,
-			Map<String, Object> properties) throws Exception {
-
-		Properties jdbcProps = getJdbcProps(pid, properties);
-		Map<String, Object> jpaProps = getJPAProps(pid, properties);
-
-		try {
-			LifecycleAware worker;
-			if(properties.containsKey(OSGI_JDBC_DRIVER_CLASS) ||
-					properties.containsKey(DSF_TARGET_FILTER)) {
-				worker = new ManagedJPADataSourceSetup(context, pid, jdbcProps, jpaProps, properties);
-			} else {
-				if(!jdbcProps.isEmpty()) {
-					LOG.warn("The configuration {} contains raw JDBC configuration, but no osgi.jdbc.driver.class or aries.dsf.target.filter properties. No DataSourceFactory will be used byt this bundle, so the JPA provider must be able to directly create the datasource, and these configuration properties will likely be ignored. {}",
-								pid, jdbcProps.stringPropertyNames());
-				}
-				worker = new ManagedJPAEMFLocator(context, pid, jpaProps, properties, null);
-			}
-			return worker;
-		} catch (InvalidSyntaxException e) {
-			LOG.error("The configuration {} contained an invalid target filter {}", pid, e.getFilter());
-			throw new ConfigurationException(DSF_TARGET_FILTER, "The target filter was invalid", e);
-		}
-	}
-
-	@SuppressWarnings("unchecked")
-	private Properties getJdbcProps(String pid, Map<String, Object> properties) throws ConfigurationException {
-
-		Object object = properties.getOrDefault(JDBC_PROP_NAMES, JDBC_PROPERTIES);
-		Collection<String> propnames;
-		if (object instanceof String) {
-			propnames = Arrays.asList(((String) object).split(","));
-		} else if (object instanceof String[]) {
-			propnames = Arrays.asList((String[]) object);
-		} else if (object instanceof Collection) {
-			propnames = (Collection<String>) object;
-		} else {
-			LOG.error("The configuration {} contained an invalid list of JDBC property names", pid, object);
-			throw new ConfigurationException(JDBC_PROP_NAMES,
-					"The jdbc property names must be a String+ or comma-separated String");
-		}
-
-		Properties p = new Properties();
-
-		propnames.stream().filter(properties::containsKey)
-				.forEach(s -> p.setProperty(s, String.valueOf(properties.get(s))));
-
-		return p;
-	}
-
-	@SuppressWarnings("unchecked")
-	private Map<String, Object> getJPAProps(String pid, Map<String, Object> properties) throws ConfigurationException {
-		
-		Object object = properties.getOrDefault(JPA_PROP_NAMES, new AllCollection());
-		Collection<String> propnames;
-		if (object instanceof String) {
-			propnames = Arrays.asList(((String) object).split(","));
-		} else if (object instanceof String[]) {
-			propnames = Arrays.asList((String[]) object);
-		} else if (object instanceof Collection) {
-			propnames = (Collection<String>) object;
-		} else {
-			LOG.error("The configuration {} contained an invalid list of JPA property names", pid, object);
-			throw new ConfigurationException(JDBC_PROP_NAMES,
-					"The jpa property names must be empty, a String+, or a comma-separated String list");
-		}
-		
-		Map<String, Object> result = properties.keySet().stream()
-			.filter(propnames::contains)
-			.collect(toMap(identity(), properties::get));
-		
-		result.putIfAbsent("javax.persistence.transactionType", JTA.name());
-		
-		return result;
-	}
-
-	private static class AllCollection implements Collection<String> {
-
-		@Override
-		public int size() {
-			return MAX_VALUE;
-		}
-
-		@Override
-		public boolean isEmpty() {
-			return false;
-		}
-
-		@Override
-		public boolean contains(Object o) {
-			return true;
-		}
-
-		@Override
-		public Iterator<String> iterator() {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public Object[] toArray() {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public <T> T[] toArray(T[] a) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public boolean add(String e) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public boolean remove(Object o) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public boolean containsAll(Collection<?> c) {
-			return true;
-		}
-
-		@Override
-		public boolean addAll(Collection<? extends String> c) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public boolean removeAll(Collection<?> c) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public boolean retainAll(Collection<?> c) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public void clear() {
-			throw new UnsupportedOperationException();
-		}
-		
-	}
-}
diff --git a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
new file mode 100644
index 0000000..876c0bc
--- /dev/null
+++ b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
@@ -0,0 +1,83 @@
+/*
+ * 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.jpa.xa.impl;
+
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.USE_DRIVER;
+
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import org.apache.aries.tx.control.jdbc.xa.connection.impl.XADataSourceMapper;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPADataSourceSetup;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.osgi.service.transaction.control.TransactionException;
+
+import com.zaxxer.hikari.HikariDataSource;
+
+public class XAJPADataSourceSetup extends AbstractManagedJPADataSourceSetup {
+
+	private static final String JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE = "javax.persistence.nonJtaDataSource";
+	
+	public XAJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties,
+			Map<String, Object> baseJPAProperties, Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
+		super(context, pid, jdbcProperties, baseJPAProperties, providerProperties);
+	}
+
+	@Override
+	protected Map<String, Object> decorateJPAProperties(DataSourceFactory dsf, Map<String, Object> providerProperties,
+			Properties jdbcProperties, Map<String, Object> jpaProperties) throws Exception {
+		DataSource unpooled;
+		try {
+			if (toBoolean(providerProperties, USE_DRIVER, false)) {
+				throw new TransactionException("The Database must use an XA connection");
+			} else {
+				unpooled = new XADataSourceMapper(dsf.createXADataSource(jdbcProperties));
+			}
+		} catch (SQLException sqle) {
+			throw new TransactionException("Unable to create the JDBC resource provider", sqle);
+		}
+
+		DataSource toUse = poolIfNecessary(providerProperties, unpooled);
+		
+		jpaProperties.put("javax.persistence.jtaDataSource", toUse);
+		
+		return jpaProperties;
+	}
+
+	@Override
+	protected void cleanupOnClose(Map<String, Object> jpaProperties) {
+		Object o = jpaProperties.get(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE);
+		if (o instanceof HikariDataSource) {
+			((HikariDataSource)o).close();
+		}
+	}
+
+	@Override
+	protected AbstractManagedJPAEMFLocator getManagedJPAEMFLocator(BundleContext context, String pid,
+			Map<String, Object> jpaProps, Map<String, Object> providerProperties, Runnable onClose) throws Exception {
+		return new XAJPAEMFLocator(context, pid, jpaProps, providerProperties, onClose);
+	}
+}
\ No newline at end of file
diff --git a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
new file mode 100644
index 0000000..4a4c800
--- /dev/null
+++ b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
@@ -0,0 +1,200 @@
+/*
+ * 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.jpa.xa.impl;
+
+import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_PROVIDER;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.persistence.spi.PersistenceProvider;
+
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+import org.osgi.service.transaction.control.TransactionControl;
+
+public class XAJPAEMFLocator extends AbstractManagedJPAEMFLocator {
+
+	public XAJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
+			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
+		super(context, pid, jpaProperties, providerProperties, onClose);
+	}
+
+	@Override
+	protected AbstractJPAEntityManagerProvider getResourceProvider(BundleContext context,
+			EntityManagerFactoryBuilder service, ServiceReference<EntityManagerFactoryBuilder> reference,
+			Map<String, Object> jpaProperties, Map<String, Object> providerProperties, Runnable onClose) {
+		return new DelayedJPAEntityManagerProvider(t -> {
+			
+			Map<String, Object> jpaProps = new HashMap<String, Object>(jpaProperties);
+			Map<String, Object> providerProps = new HashMap<String, Object>(providerProperties);
+			
+			setupTransactionManager(context, jpaProps, providerProps, t, reference);
+			
+			return new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
+					jpaProps, providerProps, t, onClose);
+		});
+	}
+
+	private void setupTransactionManager(BundleContext context, Map<String, Object> props, 
+			Map<String, Object> providerProps, ThreadLocal<TransactionControl> t, ServiceReference<EntityManagerFactoryBuilder> reference) {
+		String provider = (String) reference.getProperty(JPA_UNIT_PROVIDER);
+		
+		ServiceReference<PersistenceProvider> providerRef = getPersistenceProvider(provider, context);
+		
+		if(providerRef == null) {
+			// TODO log a warning and give up
+			return;
+		}
+
+		Bundle providerBundle = providerRef.getBundle();
+		Bundle txControlProviderBundle = context.getBundle();
+		
+		try {
+			if("org.hibernate.jpa.HibernatePersistenceProvider".equals(provider)) {
+				
+				try{
+					providerBundle.loadClass("org.hibernate.resource.transaction.TransactionCoordinatorBuilder");
+				} catch (Exception e) {
+					BundleWiring wiring = providerBundle.adapt(BundleWiring.class);
+					providerBundle = wiring.getRequiredWires("osgi.wiring.package").stream()
+								.filter(bw -> "org.hibernate".equals(bw.getCapability().getAttributes().get("osgi.wiring.package")))
+								.map(BundleWire::getProviderWiring)
+								.map(BundleWiring::getBundle)
+								.findFirst().get();
+				}
+				
+				ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
+				
+				Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.hibernate.impl.HibernateTxControlPlatform");
+				Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
+					.newInstance(t);
+				
+				props.put("hibernate.transaction.coordinator_class", plugin);
+				
+			} else if("org.apache.openjpa.persistence.PersistenceProviderImpl".equals(provider)) {
+					
+				ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
+					
+				Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.openjpa.impl.OpenJPATxControlPlatform");
+				Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
+						.newInstance(t);
+					
+				props.put("openjpa.ManagedRuntime", plugin);
+					
+			} else if("org.eclipse.persistence.jpa.PersistenceProvider".equals(provider)) {
+				
+				ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
+				
+				Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.eclipse.impl.EclipseTxControlPlatform");
+				
+				pluginClazz.getMethod("setTransactionControl", ThreadLocal.class)
+						.invoke(null, t);
+				
+				props.put("eclipselink.target-server", pluginClazz.getName());
+				props.put("org.apache.aries.jpa.eclipselink.plugin.types", pluginClazz);
+				// This is needed to ensure that sequences can be generated in nested
+				// transactions without blowing up.
+				if(!props.containsKey("eclipselink.jdbc.sequence-connection-pool")) {
+					props.put("eclipselink.jdbc.sequence-connection-pool", "true");
+				}
+				
+			} else {
+				// TODO log a warning and give up
+				return;
+			} 
+		} catch (Exception e) {
+			//TODO log a warning and give up
+			e.printStackTrace();
+		}
+	}
+
+	private ClassLoader getPluginLoader(Bundle providerBundle, Bundle txControlProviderBundle) {
+		return new ClassLoader() {
+
+			ConcurrentMap<String, Class<?>> loaded = new ConcurrentHashMap<>();
+			
+			@Override
+			public Class<?> loadClass(String name) throws ClassNotFoundException {
+				if(name.startsWith("org.apache.aries.tx.control.jpa.xa.hibernate") ||
+					name.startsWith("org.apache.aries.tx.control.jpa.xa.openjpa") ||
+					name.startsWith("org.apache.aries.tx.control.jpa.xa.eclipse")) {
+					
+					Class<?> c = loaded.get(name);
+					
+					if(c != null) {
+						return c;
+					}
+					
+					String resource = name.replace('.', '/') + ".class";
+					
+					try (InputStream is = txControlProviderBundle.getResource(resource).openStream()) {
+						ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+						byte[] b = new byte[4096];
+						int read;
+						while ((read = is.read(b)) != -1) {
+							baos.write(b, 0, read);
+						}
+						byte[] clazzBytes = baos.toByteArray();
+						c = defineClass(name, clazzBytes, 0, clazzBytes.length, 
+								XAJPAEMFLocator.class.getProtectionDomain());
+						loaded.putIfAbsent(name, c);
+						return c;
+					} catch (IOException e) {
+						throw new ClassNotFoundException("Unable to load class " + name, e);
+					}
+				}
+				
+				if(name.startsWith("org.apache.aries.tx.control") ||
+						name.startsWith("org.osgi.service.transaction.control")) {
+					return txControlProviderBundle.loadClass(name);
+				}
+				return providerBundle.loadClass(name);
+			}
+		};
+	}
+
+	private ServiceReference<PersistenceProvider> getPersistenceProvider(String provider, BundleContext context) {
+		if(provider == null) {
+			return null;
+		}
+		try {
+			return context.getServiceReferences(PersistenceProvider.class, 
+							"(javax.persistence.provider=" + provider + ")").stream()
+								.findFirst()
+								.orElse(null);
+		} catch (InvalidSyntaxException e) {
+			//TODO log a warning
+			return null;
+		} 
+	}
+}
\ No newline at end of file
diff --git a/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAManagedServiceFactory.java b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAManagedServiceFactory.java
new file mode 100644
index 0000000..bb70f5d
--- /dev/null
+++ b/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAManagedServiceFactory.java
@@ -0,0 +1,60 @@
+/*
+ * 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.jpa.xa.impl;
+
+import java.util.Map;
+import java.util.Properties;
+
+import javax.persistence.spi.PersistenceUnitTransactionType;
+
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAManagedServiceFactory;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPADataSourceSetup;
+import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.ConfigurationException;
+
+public class XAJPAManagedServiceFactory extends AbstractJPAManagedServiceFactory {
+
+	public XAJPAManagedServiceFactory(BundleContext context) {
+		super(context);
+	}
+
+	@Override
+	public String getName() {
+		return "Aries JPAEntityManagerProvider (XA only) service";
+	}
+
+	@Override
+	protected AbstractManagedJPADataSourceSetup dataSourceTracking(BundleContext context, String pid, Map<String, Object> properties,
+			Properties jdbcProps, Map<String, Object> jpaProps) throws InvalidSyntaxException, ConfigurationException {
+		return new XAJPADataSourceSetup(context, pid, jdbcProps, jpaProps, properties);
+	}
+
+	@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);
+	}
+
+	@Override
+	protected PersistenceUnitTransactionType getTransactionType() {
+		return PersistenceUnitTransactionType.JTA;
+	}
+}