[tx-control] Add recovery support to the XA JDBC provider

git-svn-id: https://svn.apache.org/repos/asf/aries/trunk/tx-control@1751616 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tx-control-api/src/main/java/org/osgi/service/transaction/control/jdbc/JDBCConnectionProviderFactory.java b/tx-control-api/src/main/java/org/osgi/service/transaction/control/jdbc/JDBCConnectionProviderFactory.java
index 89321d1..fac8ccb 100644
--- a/tx-control-api/src/main/java/org/osgi/service/transaction/control/jdbc/JDBCConnectionProviderFactory.java
+++ b/tx-control-api/src/main/java/org/osgi/service/transaction/control/jdbc/JDBCConnectionProviderFactory.java
@@ -86,6 +86,11 @@
 	 * held in the pool
 	 */
 	public static final String	USE_DRIVER					= "osgi.use.driver";
+	
+	/**
+	 * The property used to set the recovery identifier that should be used
+	 */
+	public static String OSGI_RECOVERY_IDENTIFIER = "osgi.recovery.identifier";
 
 	/**
 	 * Create a private {@link JDBCConnectionProvider} using a
diff --git a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Config.java b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Config.java
index cb71589..635f790 100644
--- a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Config.java
+++ b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Config.java
@@ -34,6 +34,10 @@
 			description="The password to pass to the DataSourceFactory (not visible as a service property)")
 	String password();
 	
+	// Recovery configuration
+	@AttributeDefinition(required=false, description="The recovery identifier for this resource. If not set then this resource will not be recoverable. This identifier must uniquely identify a single resource, and must not change if the framework is restarted.")
+	String osgi_recovery_identifier();
+	
 	// Pool configuration properties
 	
 	@AttributeDefinition(required=false, description="Is connection pooling enabled for this JDBCResourceProvider")
@@ -54,6 +58,15 @@
 	@AttributeDefinition(required=false, description="The maximum time (in ms) that a connection will stay in the pool before being discarded")
 	long osgi_connection_lifetime() default 10800000;
 	
+	// Recovery credential configuration
+	
+	@AttributeDefinition(required=false, description="The user that should be used for recovery. If not specified then recovery will use the same user credentials as normal operation")
+	String recovery_user();
+	
+	@AttributeDefinition(type=AttributeType.PASSWORD, required=false, 
+			description="The password that should be used for recovery. Only used if recovery.user is specified")
+	String _recovery_password();
+	
 	// Transaction integration configuration
 	
 	@AttributeDefinition(required=false, description="Should this Resource participate in transactions using XA")
diff --git a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
index 9118ad2..28204db 100644
--- a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
+++ b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
@@ -38,14 +38,18 @@
 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;
 
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 
 public class JDBCConnectionProviderFactoryImpl implements JDBCConnectionProviderFactory {
 
+	private static final Logger LOG = LoggerFactory.getLogger(ManagedServiceFactoryImpl.class);
+	
 	@Override
-	public JDBCConnectionProvider getProviderFor(DataSourceFactory dsf, Properties jdbcProperties,
+	public JDBCConnectionProviderImpl getProviderFor(DataSourceFactory dsf, Properties jdbcProperties,
 			Map<String, Object> resourceProviderProperties) {
 
 		boolean xaEnabled = toBoolean(resourceProviderProperties, XA_ENLISTMENT_ENABLED, true);
@@ -69,8 +73,21 @@
 		}
 
 		DataSource toUse = poolIfNecessary(resourceProviderProperties, unpooled);
+		
+		return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled, 
+				getRecoveryId(resourceProviderProperties, xaEnabled));
+	}
 
-		return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled);
+	private String getRecoveryId(Map<String, Object> resourceProviderProps, boolean xaEnabled) {
+		String recoveryIdentifier = ofNullable(resourceProviderProps)
+										.map(m -> m.get(OSGI_RECOVERY_IDENTIFIER))
+										.map(String::valueOf)
+										.orElse(null);
+		
+		if(recoveryIdentifier != null && !xaEnabled) {
+			LOG.warn("A recovery identifier {} has been declared, but the JDBCConnectionProvider is configured to disable XA", recoveryIdentifier);
+		}
+		return recoveryIdentifier;
 	}
 
 	@Override
@@ -83,7 +100,8 @@
 			DataSource toUse = poolIfNecessary(resourceProviderProperties, xaEnabled ?
 					new XADataSourceMapper(ds.unwrap(XADataSource.class)) : ds);
 	
-			return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled);
+			return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled, 
+					getRecoveryId(resourceProviderProperties, xaEnabled));
 		} catch (SQLException sqle) {
 			throw new TransactionException("Unable to create the JDBC resource provider", sqle);
 		}
@@ -101,7 +119,8 @@
 		DataSource toUse = poolIfNecessary(resourceProviderProperties, 
 				new DriverDataSource(driver, jdbcProperties.getProperty(JDBC_URL), jdbcProperties));
 		
-		return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled);
+		return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled, 
+				getRecoveryId(resourceProviderProperties, xaEnabled));
 	}
 
 	@Override
@@ -115,7 +134,7 @@
 		DataSource unpooled = new XADataSourceMapper(ds);
 		
 		return new JDBCConnectionProviderImpl(poolIfNecessary(resourceProviderProperties, unpooled),
-				xaEnabled, localEnabled);
+				xaEnabled, localEnabled, getRecoveryId(resourceProviderProperties, xaEnabled));
 	}
 
 	private void checkEnlistment(boolean xaEnabled, boolean localEnabled, boolean isXA) {
@@ -153,7 +172,7 @@
 		return toUse;
 	}
 
-	private boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
+	static boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
 		Object o =  ofNullable(props)
 			.map(m -> m.get(key))
 			.orElse(defaultValue);
diff --git a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
index ec95513..9adc9af 100644
--- a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
+++ b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
@@ -37,18 +37,25 @@
 	
 	private final boolean localEnabled;
 	
+	private final String recoveryIdentifier;
+	
 	public JDBCConnectionProviderImpl(DataSource dataSource, boolean xaEnabled,
-			boolean localEnabled) {
+			boolean localEnabled, String recoveryIdentifier) {
 		this.dataSource = dataSource;
 		this.xaEnabled = xaEnabled;
 		this.localEnabled = localEnabled;
+		this.recoveryIdentifier = recoveryIdentifier;
 	}
 
 	@Override
 	public Connection getResource(TransactionControl txControl)
 			throws TransactionException {
 		return new XAEnabledTxContextBindingConnection(txControl, dataSource , uuid,
-				xaEnabled, localEnabled);
+				xaEnabled, localEnabled, recoveryIdentifier);
+	}
+
+	public DataSource getRawDataSource() {
+		return dataSource;
 	}
 
 }
diff --git a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
index ee18aa1..7f0ac5d 100644
--- a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
+++ b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
@@ -20,6 +20,7 @@
 
 import static java.util.Arrays.asList;
 import static java.util.Optional.ofNullable;
+import static org.apache.aries.tx.control.jdbc.xa.impl.JDBCConnectionProviderFactoryImpl.toBoolean;
 import static org.osgi.framework.Constants.OBJECTCLASS;
 import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATABASE_NAME;
 import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATASOURCE_NAME;
@@ -32,6 +33,8 @@
 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 static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.OSGI_RECOVERY_IDENTIFIER;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.XA_ENLISTMENT_ENABLED;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -43,7 +46,6 @@
 import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
 
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
@@ -53,6 +55,7 @@
 import org.osgi.service.cm.ManagedServiceFactory;
 import org.osgi.service.jdbc.DataSourceFactory;
 import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
+import org.osgi.service.transaction.control.recovery.RecoverableXAResource;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
@@ -148,8 +151,9 @@
 		private final Map<String, Object> providerProperties;
 		private final ServiceTracker<DataSourceFactory, DataSourceFactory> dsfTracker;
 
-		private final AtomicReference<DataSourceFactory> activeDsf = new AtomicReference<>();
-		private final AtomicReference<ServiceRegistration<JDBCConnectionProvider>> serviceReg = new AtomicReference<>();
+		private DataSourceFactory activeDsf;
+		private ServiceRegistration<JDBCConnectionProvider> serviceReg;
+		private ServiceRegistration<RecoverableXAResource> recoveryReg;
 
 		public ManagedJDBCResourceProvider(BundleContext context, String pid, Properties jdbcProperties,
 				Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
@@ -185,33 +189,81 @@
 		@Override
 		public DataSourceFactory addingService(ServiceReference<DataSourceFactory> reference) {
 			DataSourceFactory service = context.getService(reference);
+			return updateService(service);
+		}
 
-			updateService(service);
+		private DataSourceFactory updateService(DataSourceFactory service) {
+			boolean setDsf;
+			synchronized (this) {
+				setDsf = activeDsf == null;
+				if(setDsf)
+					activeDsf = service;
+			}
+
+			ServiceRegistration<JDBCConnectionProvider> reg = null;
+			ServiceRegistration<RecoverableXAResource> reg2 = null;
+			if (setDsf) {
+				try {
+					JDBCConnectionProviderImpl provider = new JDBCConnectionProviderFactoryImpl().getProviderFor(service,
+							jdbcProperties, providerProperties);
+					reg = context
+							.registerService(JDBCConnectionProvider.class, provider, getServiceProperties());
+
+					
+					String recoveryId = (String) providerProperties.get(OSGI_RECOVERY_IDENTIFIER);
+					if(recoveryId !=null) {
+						if(toBoolean(providerProperties, XA_ENLISTMENT_ENABLED, true)) {
+							LOG.warn("A JDBCResourceProvider has been configured with a recovery identifier {} but it has also been configured not to use XA transactions. No recovery will be available.", recoveryId);
+						} else {
+							reg2 = context.registerService(RecoverableXAResource.class, 
+											new RecoverableXAResourceImpl(recoveryId, provider, 
+													(String) providerProperties.get("recovery.user"),
+													(String) providerProperties.get(".recovery.password)")), 
+													getServiceProperties());
+						}
+					}
+					
+					ServiceRegistration<JDBCConnectionProvider> oldReg;
+					ServiceRegistration<RecoverableXAResource> oldReg2;
+					
+					synchronized (this) {
+						if(activeDsf == service) {
+							oldReg = serviceReg;
+							serviceReg = reg;
+							oldReg2 = recoveryReg;
+							recoveryReg = reg2;
+						} else {
+							oldReg = reg;
+							oldReg2 = reg2;
+						}
+					}
+					safeUnregister(oldReg);
+					safeUnregister(oldReg2);
+				} catch (Exception e) {
+					LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
+					
+					synchronized (this) {
+						if(activeDsf == service) {
+							activeDsf = null;
+						}
+					}
+					safeUnregister(reg);
+					safeUnregister(reg2);
+				}
+			}
 			return service;
 		}
 
-		private void updateService(DataSourceFactory service) {
-			boolean setDsf;
-			synchronized (this) {
-				setDsf = activeDsf.compareAndSet(null, service);
-			}
-
-			if (setDsf) {
+		private void safeUnregister(ServiceRegistration<?> reg) {
+			if(reg != null) {
 				try {
-					JDBCConnectionProvider 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");
-					}
-				} catch (Exception e) {
-					LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
-					activeDsf.compareAndSet(service, null);
+					reg.unregister();
+				} catch (IllegalStateException ise) {
+					LOG.debug("An exception occurred when unregistering a service for {}", pid);
 				}
 			}
 		}
-
+			
 		private Dictionary<String, ?> getServiceProperties() {
 			Hashtable<String, Object> props = new Hashtable<>();
 			providerProperties.keySet().stream()
@@ -229,20 +281,19 @@
 		public void removedService(ServiceReference<DataSourceFactory> reference, DataSourceFactory service) {
 			boolean dsfLeft;
 			ServiceRegistration<JDBCConnectionProvider> oldReg = null;
+			ServiceRegistration<RecoverableXAResource> oldReg2 = null;
 			synchronized (this) {
-				dsfLeft = activeDsf.compareAndSet(service, null);
+				dsfLeft = activeDsf == service;
 				if (dsfLeft) {
-					oldReg = serviceReg.getAndSet(null);
+					activeDsf = null;
+					oldReg = serviceReg;
+					oldReg2 = recoveryReg;
+					serviceReg = null;
+					recoveryReg = null;
 				}
 			}
-
-			if (oldReg != null) {
-				try {
-					oldReg.unregister();
-				} catch (IllegalStateException ise) {
-					LOG.debug("An exception occurred when unregistering a service for {}", pid);
-				}
-			}
+			safeUnregister(oldReg);
+			safeUnregister(oldReg2);
 
 			if (dsfLeft) {
 				DataSourceFactory newDSF = dsfTracker.getService();
diff --git a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/RecoverableXAResourceImpl.java b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/RecoverableXAResourceImpl.java
new file mode 100644
index 0000000..ea7e0df
--- /dev/null
+++ b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/RecoverableXAResourceImpl.java
@@ -0,0 +1,119 @@
+package org.apache.aries.tx.control.jdbc.xa.impl;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.sql.DataSource;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.osgi.service.transaction.control.recovery.RecoverableXAResource;
+
+public class RecoverableXAResourceImpl implements RecoverableXAResource {
+
+	private final String id;
+	
+	private final JDBCConnectionProviderImpl providerImpl;
+	
+	private final String recoveryUser;
+	
+	private final String recoveryPw;
+	
+	public RecoverableXAResourceImpl(String id, JDBCConnectionProviderImpl providerImpl, String recoveryUser,
+			String recoveryPw) {
+		this.id = id;
+		this.providerImpl = providerImpl;
+		this.recoveryUser = recoveryUser;
+		this.recoveryPw = recoveryPw;
+	}
+
+	@Override
+	public String getId() {
+		return id;
+	}
+
+	@Override
+	public XAResource getXAResource() throws Exception {
+		DataSource rawDataSource = providerImpl.getRawDataSource();
+
+		Connection recoveryConn;
+		if(recoveryUser != null) {
+			recoveryConn = rawDataSource.getConnection(recoveryUser, recoveryPw);
+		} else {
+			recoveryConn = rawDataSource.getConnection();
+		}
+		
+		return new CloseableXAResource(recoveryConn);
+	}
+
+	@Override
+	public void releaseXAResource(XAResource xaRes) {
+		if(xaRes instanceof CloseableXAResource) {
+			try {
+				((CloseableXAResource) xaRes).close();
+			} catch (Exception e) {
+				// This is fine, the connection has been returned
+			}
+		} else {
+			throw new IllegalArgumentException("The XAResource being returned was not created by this provider implementation");
+		}
+	}
+
+	private static class CloseableXAResource implements XAResource, AutoCloseable {
+		private final Connection conn;
+		
+		private final XAResource resource;
+		
+		public CloseableXAResource(Connection conn) throws SQLException {
+			conn.isValid(5);
+			this.conn = conn;
+			this.resource = XAEnabledTxContextBindingConnection.getXAResource(conn);
+		}
+
+		@Override
+		public void close() throws Exception {
+			conn.close();
+		}
+
+		public void commit(Xid arg0, boolean arg1) throws XAException {
+			resource.commit(arg0, arg1);
+		}
+
+		public void end(Xid arg0, int arg1) throws XAException {
+			resource.end(arg0, arg1);
+		}
+
+		public void forget(Xid arg0) throws XAException {
+			resource.forget(arg0);
+		}
+
+		public int getTransactionTimeout() throws XAException {
+			return resource.getTransactionTimeout();
+		}
+
+		public boolean isSameRM(XAResource arg0) throws XAException {
+			return resource.isSameRM(arg0);
+		}
+
+		public int prepare(Xid arg0) throws XAException {
+			return resource.prepare(arg0);
+		}
+
+		public Xid[] recover(int arg0) throws XAException {
+			return resource.recover(arg0);
+		}
+
+		public void rollback(Xid arg0) throws XAException {
+			resource.rollback(arg0);
+		}
+
+		public boolean setTransactionTimeout(int arg0) throws XAException {
+			return resource.setTransactionTimeout(arg0);
+		}
+
+		public void start(Xid arg0, int arg1) throws XAException {
+			resource.start(arg0, arg1);
+		}
+	}
+}
diff --git a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java
index 764bb0c..78b5d0a 100644
--- a/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java
+++ b/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java
@@ -43,14 +43,17 @@
 	private final DataSource			dataSource;
 	private final boolean				xaEnabled;
 	private final boolean				localEnabled;
+	private final String				recoveryIdentifier;
 
 	public XAEnabledTxContextBindingConnection(TransactionControl txControl,
-			DataSource dataSource, UUID resourceId, boolean xaEnabled, boolean localEnabled) {
+			DataSource dataSource, UUID resourceId, boolean xaEnabled, boolean localEnabled,
+			String recoveryIdentifier) {
 		this.txControl = txControl;
 		this.dataSource = dataSource;
 		this.resourceId = resourceId;
 		this.xaEnabled = xaEnabled;
 		this.localEnabled = localEnabled;
+		this.recoveryIdentifier = recoveryIdentifier;
 	}
 
 	@Override
@@ -79,7 +82,7 @@
 			} else if (txContext.supportsXA() && xaEnabled) {
 				toClose = dataSource.getConnection();
 				toReturn = new TxConnectionWrapper(toClose);
-				txContext.registerXAResource(getXAResource(toClose), null);
+				txContext.registerXAResource(getXAResource(toClose), recoveryIdentifier);
 			} else if (txContext.supportsLocal() && localEnabled) {
 				toClose = dataSource.getConnection();
 				toReturn = new TxConnectionWrapper(toClose);
@@ -109,7 +112,7 @@
 	}
 
 	
-	private XAResource getXAResource(Connection conn) throws SQLException {
+	static XAResource getXAResource(Connection conn) throws SQLException {
 		if(conn instanceof XAConnectionWrapper) {
 			return ((XAConnectionWrapper)conn).getXaResource();
 		} else if(conn.isWrapperFor(XAConnectionWrapper.class)){
diff --git a/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java b/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
index d447d79..870c6f9 100644
--- a/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
+++ b/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
@@ -90,9 +90,9 @@
 		Mockito.when(xaMock.getConnection()).thenReturn(rawConnection);
 		Mockito.when(xaMock.getXAResource()).thenReturn(xaResource);
 		
-		localConn = new XAEnabledTxContextBindingConnection(control, dataSource, id, false, true);
+		localConn = new XAEnabledTxContextBindingConnection(control, dataSource, id, false, true, null);
 		xaConn = new XAEnabledTxContextBindingConnection(control, new XADataSourceMapper(xaDataSource), 
-				id, true, false);
+				id, true, false, null);
 	}
 	
 	private void setupNoTransaction() {
diff --git a/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java b/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
index 218f977..c4db2ec 100644
--- a/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
+++ b/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
@@ -20,6 +20,7 @@
 
 import static org.apache.aries.tx.control.service.xa.impl.Activator.ChangeType.RECREATE;
 import static org.apache.aries.tx.control.service.xa.impl.Activator.ChangeType.SERVICE_PROPS;
+import static org.apache.aries.tx.control.service.xa.impl.LocalResourceSupport.DISABLED;
 import static org.apache.aries.tx.control.service.xa.impl.LocalResourceSupport.ENFORCE_SINGLE;
 
 import java.io.File;
@@ -221,7 +222,7 @@
 			.forEach(e -> props.put(e.getKey(), e.getValue()));
 		
 		props.put("osgi.xa.enabled", Boolean.TRUE);
-		props.put("osgi.local.enabled", Boolean.TRUE);
+		props.put("osgi.local.enabled", getLocalResourceSupport() != DISABLED);
 		props.put(Constants.SERVICE_DESCRIPTION, "The Apache Aries Transaction Control Service for XA Transactions");
 		props.put(Constants.SERVICE_VENDOR, "Apache Aries");
 		
@@ -237,7 +238,7 @@
 	 * @return
 	 */
 	public synchronized ChangeType changed(Map<String, Object> updated, boolean isRegistered) {
-		Map<String, Object> current = filterFixedProps(updated);
+		Map<String, Object> current = filterFixedProps(config);
 		Map<String, Object> replacement = filterFixedProps(updated);
 		
 		// If our configuration is unchanged then just issue a service property update