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

[aries-tx-control] branch master updated (f6cb1ac -> 4f155dd)

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

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


    from f6cb1ac  Tx Control spec compliance - support passed in JDBC resource providers in the local JPA implementation
     new 1ded5dc  Tx Control spec compliance - recovery should work for JDBC resources created from the factory service
     new 4f155dd  Tx Control spec compliance - the XA JPA provider must support recovery to be the reference implementation

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../aries/tx/control/jdbc/xa/impl/Activator.java   | 10 ++--
 .../xa/impl/JDBCConnectionProviderFactoryImpl.java | 19 +++++--
 .../jdbc/xa/impl/JDBCConnectionProviderImpl.java   | 46 +++++++++++++++-
 .../jdbc/xa/impl/ManagedServiceFactoryImpl.java    | 30 +----------
 .../XAEnabledTxContextBindingConnectionTest.java   |  4 +-
 .../aries/tx/control/jpa/xa/impl/Activator.java    |  5 +-
 .../impl/JPAEntityManagerProviderFactoryImpl.java  | 61 ++++++++++++++++++----
 .../jpa/xa/impl/JPAEntityManagerProviderImpl.java  | 45 +++++++++++++++-
 .../jpa}/xa/impl/RecoverableXAResourceImpl.java    | 15 +++---
 .../xa/impl/XATxContextBindingEntityManager.java   |  2 +-
 .../impl/XATxContextBindingEntityManagerTest.java  |  2 +-
 11 files changed, 178 insertions(+), 61 deletions(-)
 copy tx-control-providers/{jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc => jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa}/xa/impl/RecoverableXAResourceImpl.java (87%)

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

[aries-tx-control] 02/02: Tx Control spec compliance - the XA JPA provider must support recovery to be the reference implementation

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4f155dd818e87b584244647f18893d7c460b6c05
Author: Tim Ward <ti...@apache.org>
AuthorDate: Thu Jan 25 12:05:34 2018 -0500

    Tx Control spec compliance - the XA JPA provider must support recovery to be the reference implementation
---
 .../aries/tx/control/jpa/xa/impl/Activator.java    |   5 +-
 .../impl/JPAEntityManagerProviderFactoryImpl.java  |  61 ++++++++--
 .../jpa/xa/impl/JPAEntityManagerProviderImpl.java  |  45 ++++++-
 .../jpa/xa/impl/RecoverableXAResourceImpl.java     | 135 +++++++++++++++++++++
 .../xa/impl/XATxContextBindingEntityManager.java   |   2 +-
 .../impl/XATxContextBindingEntityManagerTest.java  |   2 +-
 6 files changed, 236 insertions(+), 14 deletions(-)

diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java
index 9686e7f..50be0fe 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/Activator.java
@@ -18,6 +18,8 @@
  */
 package org.apache.aries.tx.control.jpa.xa.impl;
 
+import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.XA_ENLISTMENT_ENABLED;
+
 import java.util.Dictionary;
 import java.util.Hashtable;
 
@@ -49,7 +51,8 @@ public class Activator extends JPAResourceActivator {
 	@Override
 	protected Dictionary<String, Object> getServiceProperties() {
 		Dictionary<String, Object> props = new Hashtable<>();
-		props.put("osgi.xa.enabled", Boolean.TRUE);
+		props.put(XA_ENLISTMENT_ENABLED, Boolean.TRUE);
+		props.put("osgi.recovery.enabled", Boolean.TRUE);
 		return props;
 	}
 
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java
index 51c112c..c171faf 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderFactoryImpl.java
@@ -24,6 +24,7 @@ import static org.apache.aries.tx.control.jpa.xa.impl.XAJPADataSourceSetup.JTA_D
 import static org.apache.aries.tx.control.jpa.xa.impl.XAJPADataSourceSetup.NON_JTA_DATA_SOURCE;
 import static org.osgi.service.transaction.control.TransactionStatus.NO_TRANSACTION;
 import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.LOCAL_ENLISTMENT_ENABLED;
+import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.OSGI_RECOVERY_IDENTIFIER;
 import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.PRE_ENLISTED_DB_CONNECTION;
 import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.TRANSACTIONAL_DB_CONNECTION;
 import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.XA_ENLISTMENT_ENABLED;
@@ -112,6 +113,11 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 		Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> create;
 		JDBCConnectionProvider provider = (JDBCConnectionProvider) resourceProviderProperties.get(TRANSACTIONAL_DB_CONNECTION);
 		
+		if(resourceProviderProperties.containsKey(OSGI_RECOVERY_IDENTIFIER)) {
+			LOGGER.warn("Unable to set a recovery identifier {}. This should be set when creating the JDBC Resource Provider", 
+					resourceProviderProperties.remove(OSGI_RECOVERY_IDENTIFIER));
+		}
+		
 		create = tx -> {
 				jpaPropsToUse.put(JTA_DATA_SOURCE, 
 					new ScopedConnectionDataSource(provider.getResource(tx.get())));
@@ -131,6 +137,12 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 			LOGGER.error("No datasource supplied in the configuration");
 			throw new IllegalArgumentException("No pre-enlisted datasource could be found to create the EntityManagerFactory. Please provide either a javax.persistence.jtaDataSource");
 		}
+		
+		if(resourceProviderProperties.containsKey(OSGI_RECOVERY_IDENTIFIER)) {
+			LOGGER.warn("Unable to set a recovery identifier {} for a pre-enlisted connection", 
+					resourceProviderProperties.remove(OSGI_RECOVERY_IDENTIFIER));
+		}
+		
 		create = tx -> {
 			DataSource toUse = JPADataSourceHelper.poolIfNecessary(resourceProviderProperties, (DataSource) supplied);
 			jpaPropsToUse.put(JTA_DATA_SOURCE, toUse);
@@ -200,14 +212,14 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 		Map<String, Object> resourceProviderProperties, ThreadLocal<TransactionControl> localStore, Runnable onClose) {
 		Map<String, Object> toUse;
 		if(checkEnlistment(resourceProviderProperties)) {
-			toUse = enlistDataSource(localStore, jpaProperties);
+			toUse = enlistDataSource(localStore, jpaProperties, resourceProviderProperties);
 		} else {
 			toUse = jpaProperties;
 		}
 		
 		setupTransactionManager(context, toUse, localStore, emfb);
 		
-		return localStore.get().notSupported(() -> internalBuilderCreate(emfb, toUse, localStore, onClose));
+		return localStore.get().notSupported(() -> internalBuilderCreate(emfb, toUse, resourceProviderProperties, localStore, onClose));
 	}
 
 	private boolean checkEnlistment(Map<String, Object> resourceProviderProperties) {
@@ -221,11 +233,12 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 		return !toBoolean(resourceProviderProperties, PRE_ENLISTED_DB_CONNECTION, false);
 	}
 
-	private Map<String, Object> enlistDataSource(ThreadLocal<TransactionControl> tx, Map<String, Object> jpaProperties) {
+	private Map<String, Object> enlistDataSource(ThreadLocal<TransactionControl> tx, Map<String, Object> jpaProperties, Map<String, Object> resourceProviderProperties) {
 		Map<String, Object> toReturn = new HashMap<>(jpaProperties);
 		
 		DataSource enlistedDS = new EnlistingDataSource(tx, 
-				(DataSource)jpaProperties.get(JTA_DATA_SOURCE));
+				(DataSource)jpaProperties.get(JTA_DATA_SOURCE),
+				(String) resourceProviderProperties.get(OSGI_RECOVERY_IDENTIFIER));
 		
 		toReturn.put(JTA_DATA_SOURCE, enlistedDS);
 		
@@ -356,7 +369,8 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 	}
 
 	private AbstractJPAEntityManagerProvider internalBuilderCreate(EntityManagerFactoryBuilder emfb,
-			Map<String, Object> jpaProperties, ThreadLocal<TransactionControl> tx, Runnable onClose) {
+			Map<String, Object> jpaProperties, Map<String, Object> providerProperties, 
+			ThreadLocal<TransactionControl> tx, Runnable onClose) {
 		EntityManagerFactory emf = emfb.createEntityManagerFactory(jpaProperties);
 		
 		validateEMF(emf);
@@ -369,7 +383,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 			if (onClose != null) {
 				onClose.run();
 			}
-		});
+		}, context, jpaProperties, providerProperties);
 	}
 
 	private void validateEMF(EntityManagerFactory emf) {
@@ -396,7 +410,13 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 		checkEnlistment(resourceProviderProperties);
 		validateEMF(emf);
 		
-		return new JPAEntityManagerProviderImpl(emf, new ThreadLocal<>(), null);
+		if(resourceProviderProperties.containsKey(OSGI_RECOVERY_IDENTIFIER)) {
+			LOGGER.warn("Unable to set a recovery identifier {} for an existing EntityManagerFactory", 
+					resourceProviderProperties.remove(OSGI_RECOVERY_IDENTIFIER));
+		}
+		
+		return new JPAEntityManagerProviderImpl(emf, new ThreadLocal<>(), null, null,
+				null, null);
 	}
 
 	public static boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
@@ -460,14 +480,18 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 	public static class EnlistingDataSource implements DataSource {
 		
 		private final DataSource delegate;
+		
+		private final String recoveryIdentifier;
 
 		private final UUID resourceId = UUID.randomUUID();
 		
 		private final ThreadLocal<TransactionControl> txControlToUse;
 		
-		public EnlistingDataSource(ThreadLocal<TransactionControl> txControlToUse, DataSource delegate) {
+		public EnlistingDataSource(ThreadLocal<TransactionControl> txControlToUse, 
+				DataSource delegate, String recoveryIdentifier) {
 			this.txControlToUse = txControlToUse;
 			this.delegate = delegate;
+			this.recoveryIdentifier = recoveryIdentifier;
 		}
 		
 		public TransactionControl getTxControl() {
@@ -498,6 +522,23 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 			return enlistedConnection(() -> delegate.getConnection());
 		}
 
+		/**
+		 * Used by the {@link RecoverableXAResourceImpl}
+		 * @return
+		 * @throws SQLException
+		 */
+		Connection getRawConnection() throws SQLException {
+			return delegate.getConnection();
+		}
+		/**
+		 * Used by the {@link RecoverableXAResourceImpl}
+		 * @return
+		 * @throws SQLException
+		 */
+		Connection getRawConnection(String username, String password) throws SQLException {
+			return delegate.getConnection(username, password);
+		}
+
 		public void setLoginTimeout(int seconds) throws SQLException {
 			delegate.setLoginTimeout(seconds);
 		}
@@ -537,7 +578,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 					toReturn = new ScopedConnectionWrapper(toClose);
 				} else if (txContext.supportsXA()) {
 					toReturn = new TxConnectionWrapper(toClose);
-					txContext.registerXAResource(getXAResource(toClose), null);
+					txContext.registerXAResource(getXAResource(toClose), recoveryIdentifier);
 				} else {
 					throw new TransactionException(
 							"There is a transaction active, but it does not support XA participants");
@@ -562,7 +603,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
 			return toReturn;
 		}
 		
-		private XAResource getXAResource(Connection conn) throws SQLException {
+		public 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-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderImpl.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderImpl.java
index b040f1f..3005866 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderImpl.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/JPAEntityManagerProviderImpl.java
@@ -18,29 +18,72 @@
  */
 package org.apache.aries.tx.control.jpa.xa.impl;
 
+import static java.util.Optional.ofNullable;
+import static org.apache.aries.tx.control.jpa.xa.impl.XAJPADataSourceSetup.JTA_DATA_SOURCE;
+import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.OSGI_RECOVERY_IDENTIFIER;
+
+import java.util.Map;
 import java.util.UUID;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
 
 import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
+import org.apache.aries.tx.control.jpa.xa.impl.JPAEntityManagerProviderFactoryImpl.EnlistingDataSource;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.transaction.control.TransactionControl;
 import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.recovery.RecoverableXAResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class JPAEntityManagerProviderImpl extends AbstractJPAEntityManagerProvider {
 
+	private static final Logger LOG = LoggerFactory.getLogger(JPAEntityManagerProviderImpl.class);
+	
 	private final UUID					uuid	= UUID.randomUUID();
 
 	private final ThreadLocal<TransactionControl> tx;
+	
+	private final String recoveryIdentifier;
+	
+	private final ServiceRegistration<RecoverableXAResource> reg;
 
 	public JPAEntityManagerProviderImpl(EntityManagerFactory emf, ThreadLocal<TransactionControl> tx,
-			Runnable onClose) {
+			Runnable onClose, BundleContext ctx, Map<String, Object> jpaProps, 
+			Map<String, Object> providerProps) {
 		super(emf, onClose);
 		this.tx = tx;
+		
+		recoveryIdentifier = (String) ofNullable(providerProps)
+								.map(m -> m.get(OSGI_RECOVERY_IDENTIFIER))
+								.orElse(null);
+		
+		if(recoveryIdentifier != null) {
+			EnlistingDataSource ds = (EnlistingDataSource) jpaProps.get(JTA_DATA_SOURCE);
+			reg = ctx.registerService(RecoverableXAResource.class, 
+					new RecoverableXAResourceImpl(recoveryIdentifier, ds, 
+							(String) providerProps.get("recovery.user"),
+							(String) providerProps.get(".recovery.password)")), 
+					null);
+		} else {
+			reg = null;
+		}
 	}
 
 	@Override
 	public EntityManager getResource(TransactionControl txControl) throws TransactionException {
 		return new XATxContextBindingEntityManager(txControl, this, uuid, tx);
 	}
+
+	public void unregister() {
+		if(reg != null) {
+			try {
+				reg.unregister();
+			} catch (IllegalStateException ise) {
+				LOG.debug("An exception occurred when unregistering the recovery service for {}", recoveryIdentifier);
+			}
+		}
+	}
 }
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/RecoverableXAResourceImpl.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/RecoverableXAResourceImpl.java
new file mode 100644
index 0000000..229b443
--- /dev/null
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/RecoverableXAResourceImpl.java
@@ -0,0 +1,135 @@
+/*
+ * 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.sql.Connection;
+import java.sql.SQLException;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.apache.aries.tx.control.jpa.xa.impl.JPAEntityManagerProviderFactoryImpl.EnlistingDataSource;
+import org.osgi.service.transaction.control.recovery.RecoverableXAResource;
+
+public class RecoverableXAResourceImpl implements RecoverableXAResource {
+
+	private final String id;
+	
+	private final EnlistingDataSource ds;
+	
+	private final String recoveryUser;
+	
+	private final String recoveryPw;
+	
+	public RecoverableXAResourceImpl(String id, EnlistingDataSource ds, String recoveryUser,
+			String recoveryPw) {
+		this.id = id;
+		this.ds = ds;
+		this.recoveryUser = recoveryUser;
+		this.recoveryPw = recoveryPw;
+	}
+
+	@Override
+	public String getId() {
+		return id;
+	}
+
+	@Override
+	public XAResource getXAResource() throws Exception {
+		Connection recoveryConn;
+		if(recoveryUser != null) {
+			recoveryConn = ds.getRawConnection(recoveryUser, recoveryPw);
+		} else {
+			recoveryConn = ds.getRawConnection();
+		}
+		
+		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 = EnlistingDataSource.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-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManager.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManager.java
index 73398c6..9ed4043 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManager.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManager.java
@@ -82,7 +82,7 @@ public class XATxContextBindingEntityManager extends EntityManagerWrapper {
 				toClose.joinTransaction();
 			} else {
 				throw new TransactionException(
-						"There is a transaction active, but it does not support local participants");
+						"There is a transaction active, but it does not support xa participants");
 			}
 		} catch (Exception sqle) {
 			commonTxStore.set(previous);
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/test/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManagerTest.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/test/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManagerTest.java
index 7f27829..254f3ca 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/test/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManagerTest.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/test/java/org/apache/aries/tx/control/jpa/xa/impl/XATxContextBindingEntityManagerTest.java
@@ -88,7 +88,7 @@ public class XATxContextBindingEntityManagerTest {
 		Mockito.when(context.getScopedValue(Mockito.any()))
 			.thenAnswer(i -> variables.get(i.getArguments()[0]));
 		
-		provider = new JPAEntityManagerProviderImpl(emf, commonTxControl, null);
+		provider = new JPAEntityManagerProviderImpl(emf, commonTxControl, null, null, null, null);
 		
 		em = new XATxContextBindingEntityManager(control, provider, id, commonTxControl);
 	}

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

[aries-tx-control] 01/02: Tx Control spec compliance - recovery should work for JDBC resources created from the factory service

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 1ded5dc93ec3227591e66ccc18f9d63c55163b7e
Author: Tim Ward <ti...@apache.org>
AuthorDate: Thu Jan 25 10:12:39 2018 -0500

    Tx Control spec compliance - recovery should work for JDBC resources created from the factory service
---
 .../aries/tx/control/jdbc/xa/impl/Activator.java   | 10 +++--
 .../xa/impl/JDBCConnectionProviderFactoryImpl.java | 19 +++++++--
 .../jdbc/xa/impl/JDBCConnectionProviderImpl.java   | 46 +++++++++++++++++++++-
 .../jdbc/xa/impl/ManagedServiceFactoryImpl.java    | 30 +-------------
 .../XAEnabledTxContextBindingConnectionTest.java   |  4 +-
 5 files changed, 69 insertions(+), 40 deletions(-)

diff --git a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java
index 24b121c..6f5fae5 100644
--- a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java
+++ b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/Activator.java
@@ -18,6 +18,9 @@
  */
 package org.apache.aries.tx.control.jdbc.xa.impl;
 
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.LOCAL_ENLISTMENT_ENABLED;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.XA_ENLISTMENT_ENABLED;
+
 import java.util.Dictionary;
 import java.util.Hashtable;
 
@@ -40,7 +43,7 @@ public class Activator extends ResourceActivator<AbstractJDBCConnectionProvider,
 			@Override
 			protected TrackingResourceProviderFactory<AbstractJDBCConnectionProvider> getTrackingResourceManagerProviderFactory() {
 				return new ResourceTrackingJDBCConnectionProviderFactory(
-						new JDBCConnectionProviderFactoryImpl());
+						new JDBCConnectionProviderFactoryImpl(context));
 			}
 		};
 	}
@@ -53,8 +56,9 @@ public class Activator extends ResourceActivator<AbstractJDBCConnectionProvider,
 	@Override
 	protected Dictionary<String, Object> getServiceProperties() {
 		Dictionary<String, Object> props = new Hashtable<>();
-		props.put("osgi.local.enabled", Boolean.TRUE);
-		props.put("osgi.xa.enabled", Boolean.TRUE);
+		props.put(LOCAL_ENLISTMENT_ENABLED, Boolean.TRUE);
+		props.put(XA_ENLISTMENT_ENABLED, Boolean.TRUE);
+		props.put("osgi.recovery.enabled", Boolean.TRUE);
 		return props;
 	}
 
diff --git a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
index 34c3f86..7ece898 100644
--- a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
+++ b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderFactoryImpl.java
@@ -37,6 +37,7 @@ import org.apache.aries.tx.control.jdbc.common.impl.AbstractInternalJDBCConnecti
 import org.apache.aries.tx.control.jdbc.common.impl.AbstractJDBCConnectionProvider;
 import org.apache.aries.tx.control.jdbc.common.impl.DriverDataSource;
 import org.apache.aries.tx.control.jdbc.xa.connection.impl.XADataSourceMapper;
+import org.osgi.framework.BundleContext;
 import org.osgi.service.jdbc.DataSourceFactory;
 import org.osgi.service.transaction.control.TransactionException;
 import org.slf4j.Logger;
@@ -46,6 +47,12 @@ public class JDBCConnectionProviderFactoryImpl extends AbstractInternalJDBCConne
 
 	private static final Logger LOG = LoggerFactory.getLogger(ManagedServiceFactoryImpl.class);
 	
+	private final BundleContext context;
+	
+	public JDBCConnectionProviderFactoryImpl(BundleContext context) {
+		this.context = context;
+	}
+
 	@Override
 	public JDBCConnectionProviderImpl getProviderFor(DataSourceFactory dsf, Properties jdbcProperties,
 			Map<String, Object> resourceProviderProperties) {
@@ -73,7 +80,8 @@ public class JDBCConnectionProviderFactoryImpl extends AbstractInternalJDBCConne
 		DataSource toUse = poolIfNecessary(resourceProviderProperties, unpooled);
 		
 		return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled, 
-				getRecoveryId(resourceProviderProperties, xaEnabled));
+				getRecoveryId(resourceProviderProperties, xaEnabled), context,
+				resourceProviderProperties);
 	}
 
 	private String getRecoveryId(Map<String, Object> resourceProviderProps, boolean xaEnabled) {
@@ -112,7 +120,8 @@ public class JDBCConnectionProviderFactoryImpl extends AbstractInternalJDBCConne
 				new XADataSourceMapper(xaDS) : ds);
 
 		return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled, 
-				getRecoveryId(resourceProviderProperties, xaEnabled));
+				getRecoveryId(resourceProviderProperties, xaEnabled), context,
+				resourceProviderProperties);
 	}
 
 	@Override
@@ -128,7 +137,8 @@ public class JDBCConnectionProviderFactoryImpl extends AbstractInternalJDBCConne
 				new DriverDataSource(driver, jdbcProperties.getProperty(JDBC_URL), jdbcProperties));
 		
 		return new JDBCConnectionProviderImpl(toUse, xaEnabled, localEnabled, 
-				getRecoveryId(resourceProviderProperties, xaEnabled));
+				getRecoveryId(resourceProviderProperties, xaEnabled), context,
+				resourceProviderProperties);
 	}
 
 	@Override
@@ -142,7 +152,8 @@ public class JDBCConnectionProviderFactoryImpl extends AbstractInternalJDBCConne
 		DataSource unpooled = new XADataSourceMapper(ds);
 		
 		return new JDBCConnectionProviderImpl(poolIfNecessary(resourceProviderProperties, unpooled),
-				xaEnabled, localEnabled, getRecoveryId(resourceProviderProperties, xaEnabled));
+				xaEnabled, localEnabled, getRecoveryId(resourceProviderProperties, xaEnabled), context,
+				resourceProviderProperties);
 	}
 
 	private void checkEnlistment(boolean xaEnabled, boolean localEnabled, boolean isXA) {
diff --git a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
index 130ef6a..12706e4 100644
--- a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
+++ b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/JDBCConnectionProviderImpl.java
@@ -18,17 +18,28 @@
  */
 package org.apache.aries.tx.control.jdbc.xa.impl;
 
+import static org.apache.aries.tx.control.jdbc.common.impl.AbstractInternalJDBCConnectionProviderFactory.toBoolean;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.XA_ENLISTMENT_ENABLED;
+
 import java.sql.Connection;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.sql.DataSource;
 
 import org.apache.aries.tx.control.jdbc.common.impl.AbstractJDBCConnectionProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.transaction.control.TransactionControl;
 import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.recovery.RecoverableXAResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class JDBCConnectionProviderImpl extends AbstractJDBCConnectionProvider {
 
+	private static final Logger LOG = LoggerFactory.getLogger(JDBCConnectionProviderImpl.class);
+	
 	private final UUID			uuid	= UUID.randomUUID();
 
 	private final boolean xaEnabled;
@@ -37,18 +48,49 @@ public class JDBCConnectionProviderImpl extends AbstractJDBCConnectionProvider {
 	
 	private final String recoveryIdentifier;
 	
+	private final ServiceRegistration<RecoverableXAResource> reg;
+	
 	public JDBCConnectionProviderImpl(DataSource dataSource, boolean xaEnabled,
-			boolean localEnabled, String recoveryIdentifier) {
+			boolean localEnabled, String recoveryIdentifier, BundleContext ctx,
+			Map<String, Object> providerProperties) {
 		super(dataSource);
 		this.xaEnabled = xaEnabled;
 		this.localEnabled = localEnabled;
 		this.recoveryIdentifier = recoveryIdentifier;
+		
+		if(recoveryIdentifier != 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.", recoveryIdentifier);
+				reg = null;
+			} else {
+				reg = ctx.registerService(RecoverableXAResource.class, 
+						new RecoverableXAResourceImpl(recoveryIdentifier, this, 
+								(String) providerProperties.get("recovery.user"),
+								(String) providerProperties.get(".recovery.password)")), 
+						null);
+			}
+		} else {
+			reg = null;
+		}
 	}
-
+	
 	@Override
 	public Connection getResource(TransactionControl txControl)
 			throws TransactionException {
 		return new XAEnabledTxContextBindingConnection(txControl, this, uuid,
 				xaEnabled, localEnabled, recoveryIdentifier);
 	}
+
+	@Override
+	public void close() {
+		if(reg != null) {
+			try {
+				reg.unregister();
+			} catch (IllegalStateException ise) {
+				LOG.debug("An exception occurred when unregistering the recovery service for {}", recoveryIdentifier);
+			}
+		}
+		super.close();
+	}
+	
 }
diff --git a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
index 1273ee5..d696355 100644
--- a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
+++ b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/ManagedServiceFactoryImpl.java
@@ -19,7 +19,6 @@
 package org.apache.aries.tx.control.jdbc.xa.impl;
 
 import static java.util.Arrays.asList;
-import static org.apache.aries.tx.control.jdbc.common.impl.AbstractInternalJDBCConnectionProviderFactory.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,8 +31,6 @@ 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 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;
@@ -53,7 +50,6 @@ import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.cm.ConfigurationException;
 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;
@@ -127,7 +123,6 @@ public class ManagedServiceFactoryImpl extends ConfigurationDefinedResourceFacto
 
 		private DataSourceFactory activeDsf;
 		private ServiceRegistration<JDBCConnectionProvider> serviceReg;
-		private ServiceRegistration<RecoverableXAResource> recoveryReg;
 		private AbstractJDBCConnectionProvider provider;
 
 		public ManagedJDBCResourceProvider(BundleContext context, String pid, Properties jdbcProperties,
@@ -176,46 +171,28 @@ public class ManagedServiceFactoryImpl extends ConfigurationDefinedResourceFacto
 			}
 
 			ServiceRegistration<JDBCConnectionProvider> reg = null;
-			ServiceRegistration<RecoverableXAResource> reg2 = null;
 			JDBCConnectionProviderImpl provider = null;
 			if (setDsf) {
 				try {
-					provider = new JDBCConnectionProviderFactoryImpl().getProviderFor(service,
+					provider = new JDBCConnectionProviderFactoryImpl(context).getProviderFor(service,
 							jdbcProperties, providerProperties);
-					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());
-						}
-					}
 					reg = context
 							.registerService(JDBCConnectionProvider.class, provider, getServiceProperties());
 
 					ServiceRegistration<JDBCConnectionProvider> oldReg;
-					ServiceRegistration<RecoverableXAResource> oldReg2;
 					AbstractJDBCConnectionProvider oldProvider;
 					synchronized (this) {
 						if(activeDsf == service) {
 							oldReg = serviceReg;
 							serviceReg = reg;
-							oldReg2 = recoveryReg;
-							recoveryReg = reg2;
 							oldProvider = this.provider;
 							this.provider = provider;
 						} else {
 							oldReg = reg;
-							oldReg2 = reg2;
 							oldProvider = provider;
 						}
 					}
 					safeUnregister(oldReg);
-					safeUnregister(oldReg2);
 					safeClose(oldProvider);
 				} catch (Exception e) {
 					LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
@@ -226,7 +203,6 @@ public class ManagedServiceFactoryImpl extends ConfigurationDefinedResourceFacto
 						}
 					}
 					safeUnregister(reg);
-					safeUnregister(reg2);
 					safeClose(provider);
 				}
 			}
@@ -270,22 +246,18 @@ public class ManagedServiceFactoryImpl extends ConfigurationDefinedResourceFacto
 		public void removedService(ServiceReference<DataSourceFactory> reference, DataSourceFactory service) {
 			boolean dsfLeft;
 			ServiceRegistration<JDBCConnectionProvider> oldReg = null;
-			ServiceRegistration<RecoverableXAResource> oldReg2 = null;
 			AbstractJDBCConnectionProvider oldProvider = null;
 			synchronized (this) {
 				dsfLeft = activeDsf == service;
 				if (dsfLeft) {
 					activeDsf = null;
 					oldReg = serviceReg;
-					oldReg2 = recoveryReg;
 					oldProvider = provider;
 					serviceReg = null;
-					recoveryReg = null;
 					provider = null;
 				}
 			}
 			safeUnregister(oldReg);
-			safeUnregister(oldReg2);
 			safeClose(oldProvider);
 
 			if (dsfLeft) {
diff --git a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
index bc86e2a..c94f724 100644
--- a/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
+++ b/tx-control-providers/jdbc/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
@@ -94,9 +94,9 @@ public class XAEnabledTxContextBindingConnectionTest {
 		Mockito.when(xaMock.getConnection()).thenReturn(rawConnection);
 		Mockito.when(xaMock.getXAResource()).thenReturn(xaResource);
 		
-		localProvider = new JDBCConnectionProviderImpl(dataSource, false, true, null);
+		localProvider = new JDBCConnectionProviderImpl(dataSource, false, true, null, null, null);
 		xaProvider = new JDBCConnectionProviderImpl(new XADataSourceMapper(xaDataSource), 
-				true, false, null);
+				true, false, null, null, null);
 		
 		localConn = new XAEnabledTxContextBindingConnection(control, localProvider, 
 				id, false, true, null);

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