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/24 18:39:48 UTC
[aries-tx-control] 03/03: Tx Control spec compliance - support
passed in JDBC resource providers in the local JPA implementation
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 f6cb1acfc2a529ef8538f4273ad353bd2f086de4
Author: Tim Ward <ti...@apache.org>
AuthorDate: Wed Jan 24 13:38:25 2018 -0500
Tx Control spec compliance - support passed in JDBC resource providers in the local JPA implementation
---
.../impl/DelayedJPAEntityManagerProvider.java | 3 +-
.../jpa/common/impl/JPAResourceActivator.java | 4 -
.../common}/impl/ScopedConnectionDataSource.java | 2 +-
.../impl/JPAEntityManagerProviderFactoryImpl.java | 42 ++-
.../local/impl/JPAEntityManagerProviderImpl.java | 10 +-
.../jpa/local/impl/ScopedConnectionIsolator.java | 291 +++++++++++++++++++++
...xContextBindingJDBCDelegatingEntityManager.java | 111 ++++++++
.../impl/TxContextBindingEntityManagerTest.java | 2 +-
...extBindingJDBCDelegatingEntityManagerTest.java} | 51 ++--
.../impl/JPAEntityManagerProviderFactoryImpl.java | 2 +
.../tx/control/jpa/xa/impl/XAJPAEMFLocator.java | 1 +
11 files changed, 482 insertions(+), 37 deletions(-)
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/DelayedJPAEntityManagerProvider.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DelayedJPAEntityManagerProvider.java
similarity index 94%
rename from tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/DelayedJPAEntityManagerProvider.java
rename to tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DelayedJPAEntityManagerProvider.java
index 3c3d516..71a70f5 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/DelayedJPAEntityManagerProvider.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DelayedJPAEntityManagerProvider.java
@@ -16,13 +16,12 @@
* 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 java.util.function.Function;
import javax.persistence.EntityManager;
-import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
import org.osgi.service.transaction.control.TransactionControl;
import org.osgi.service.transaction.control.TransactionException;
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAResourceActivator.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAResourceActivator.java
index 1b3ef75..600923a 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAResourceActivator.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAResourceActivator.java
@@ -20,14 +20,10 @@ package org.apache.aries.tx.control.jpa.common.impl;
import org.apache.aries.tx.control.resource.common.impl.ResourceActivator;
import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public abstract class JPAResourceActivator extends
ResourceActivator<AbstractJPAEntityManagerProvider, ResourceTrackingJPAEntityManagerProviderFactory>{
- private static final Logger LOG = LoggerFactory.getLogger(JPAResourceActivator.class);
-
@Override
protected Class<JPAEntityManagerProviderFactory> getAdvertisedInterface() {
return JPAEntityManagerProviderFactory.class;
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ScopedConnectionDataSource.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedConnectionDataSource.java
similarity index 96%
rename from tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ScopedConnectionDataSource.java
rename to tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedConnectionDataSource.java
index 11fcfa5..d52fbc2 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ScopedConnectionDataSource.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedConnectionDataSource.java
@@ -1,4 +1,4 @@
-package org.apache.aries.tx.control.jpa.xa.impl;
+package org.apache.aries.tx.control.jpa.common.impl;
import java.io.PrintWriter;
import java.sql.Connection;
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
index 9882e23..d02a3b3 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
@@ -21,20 +21,26 @@ package org.apache.aries.tx.control.jpa.local.impl;
import static java.util.Optional.ofNullable;
import static javax.persistence.spi.PersistenceUnitTransactionType.RESOURCE_LOCAL;
import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.LOCAL_ENLISTMENT_ENABLED;
+import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.TRANSACTIONAL_DB_CONNECTION;
import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.XA_ENLISTMENT_ENABLED;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Function;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
+import org.apache.aries.tx.control.jpa.common.impl.DelayedJPAEntityManagerProvider;
import org.apache.aries.tx.control.jpa.common.impl.InternalJPAEntityManagerProviderFactory;
import org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper;
+import org.apache.aries.tx.control.jpa.common.impl.ScopedConnectionDataSource;
import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+import org.osgi.service.transaction.control.TransactionControl;
import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
import com.zaxxer.hikari.HikariDataSource;
@@ -44,7 +50,12 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties,
Map<String, Object> resourceProviderProperties) {
Map<String, Object> jpaPropsToUse = jpaProperties == null ? new HashMap<>() : new HashMap<>(jpaProperties);
- checkEnlistment(resourceProviderProperties);
+ Map<String, Object> resourceProviderPropertiesToUse = resourceProviderProperties == null ? new HashMap<>() : new HashMap<>(resourceProviderProperties);
+ checkEnlistment(resourceProviderPropertiesToUse);
+
+ if(resourceProviderPropertiesToUse.containsKey(TRANSACTIONAL_DB_CONNECTION)) {
+ return delegateTransactionality(emfb, jpaPropsToUse, resourceProviderPropertiesToUse);
+ }
Object found = jpaPropsToUse.get("javax.persistence.dataSource");
@@ -63,7 +74,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
throw new IllegalArgumentException("The object found when checking the javax.persistence.dataSource and javax.persistence.nonJtaDataSource properties was not a DataSource.");
}
- DataSource toUse = JPADataSourceHelper.poolIfNecessary(resourceProviderProperties, unpooled);
+ DataSource toUse = JPADataSourceHelper.poolIfNecessary(resourceProviderPropertiesToUse, unpooled);
jpaPropsToUse.put("javax.persistence.dataSource", toUse);
jpaPropsToUse.put("javax.persistence.nonJtaDataSource", toUse);
@@ -71,7 +82,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
validateEMF(emf);
- return new JPAEntityManagerProviderImpl(emf, () -> {
+ return new JPAEntityManagerProviderImpl(emf, false, () -> {
emf.close();
if (toUse instanceof HikariDataSource) {
((HikariDataSource)toUse).close();
@@ -79,6 +90,27 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
});
}
+ private AbstractJPAEntityManagerProvider delegateTransactionality(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaPropsToUse, Map<String, Object> resourceProviderProperties) {
+ Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> create;
+ JDBCConnectionProvider provider = (JDBCConnectionProvider) resourceProviderProperties.get(TRANSACTIONAL_DB_CONNECTION);
+
+ create = tx -> {
+ DataSource ds = new ScopedConnectionDataSource(
+ new ScopedConnectionIsolator(provider.getResource(tx.get())));
+ jpaPropsToUse.put("javax.persistence.dataSource", ds);
+ jpaPropsToUse.put("javax.persistence.nonJtaDataSource", ds);
+
+ EntityManagerFactory emf = emfb.createEntityManagerFactory(jpaPropsToUse);
+
+ validateEMF(emf);
+
+ return new JPAEntityManagerProviderImpl(emf, true, () -> {
+ emf.close();
+ });
+ };
+ return new DelayedJPAEntityManagerProvider(create);
+ }
+
public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb,
Map<String, Object> jpaProperties, Map<String, Object> resourceProviderProperties,
Runnable onClose) {
@@ -88,7 +120,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
validateEMF(emf);
- return new JPAEntityManagerProviderImpl(emf, () -> {
+ return new JPAEntityManagerProviderImpl(emf, false, () -> {
try {
emf.close();
} catch (Exception e) {
@@ -123,7 +155,7 @@ public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityMan
checkEnlistment(resourceProviderProperties);
validateEMF(emf);
- return new JPAEntityManagerProviderImpl(emf, null);
+ return new JPAEntityManagerProviderImpl(emf, false, null);
}
private void checkEnlistment(Map<String, Object> resourceProviderProperties) {
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java
index d0affaa..115a5ee 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java
@@ -30,13 +30,19 @@ import org.osgi.service.transaction.control.TransactionException;
public class JPAEntityManagerProviderImpl extends AbstractJPAEntityManagerProvider {
private final UUID uuid = UUID.randomUUID();
+
+ private final boolean delegateEnlistment;
- public JPAEntityManagerProviderImpl(EntityManagerFactory emf, Runnable onClose) {
+ public JPAEntityManagerProviderImpl(EntityManagerFactory emf,
+ boolean delegateEnlistment, Runnable onClose) {
super(emf, onClose);
+ this.delegateEnlistment = delegateEnlistment;
}
@Override
public EntityManager getResource(TransactionControl txControl) throws TransactionException {
- return new TxContextBindingEntityManager(txControl, this, uuid);
+ return delegateEnlistment ?
+ new TxContextBindingJDBCDelegatingEntityManager(txControl, this, uuid) :
+ new TxContextBindingEntityManager(txControl, this, uuid);
}
}
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ScopedConnectionIsolator.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ScopedConnectionIsolator.java
new file mode 100644
index 0000000..d1f9e0a
--- /dev/null
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ScopedConnectionIsolator.java
@@ -0,0 +1,291 @@
+/*
+ * 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.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+public class ScopedConnectionIsolator implements Connection {
+
+ private final Connection connection;
+
+ public ScopedConnectionIsolator(Connection connection) {
+ this.connection = connection;
+ }
+
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ return getDelegate().unwrap(iface);
+ }
+
+ public boolean isWrapperFor(Class< ? > iface) throws SQLException {
+ return getDelegate().isWrapperFor(iface);
+ }
+
+ public Statement createStatement() throws SQLException {
+ return getDelegate().createStatement();
+ }
+
+ public PreparedStatement prepareStatement(String sql) throws SQLException {
+ return getDelegate().prepareStatement(sql);
+ }
+
+ public CallableStatement prepareCall(String sql) throws SQLException {
+ return getDelegate().prepareCall(sql);
+ }
+
+ public String nativeSQL(String sql) throws SQLException {
+ return getDelegate().nativeSQL(sql);
+ }
+
+ public void setAutoCommit(boolean autoCommit) throws SQLException {
+ // This is a no-op
+ }
+
+ public boolean getAutoCommit() throws SQLException {
+ // This is a no-op
+ return false;
+ }
+
+ public void commit() throws SQLException {
+ // This is a no-op
+ }
+
+ public void rollback() throws SQLException {
+ // This is a no-op
+ }
+
+ public void close() throws SQLException {
+ getDelegate().close();
+ }
+
+ public boolean isClosed() throws SQLException {
+ return getDelegate().isClosed();
+ }
+
+ public DatabaseMetaData getMetaData() throws SQLException {
+ return getDelegate().getMetaData();
+ }
+
+ public void setReadOnly(boolean readOnly) throws SQLException {
+ getDelegate().setReadOnly(readOnly);
+ }
+
+ public boolean isReadOnly() throws SQLException {
+ return getDelegate().isReadOnly();
+ }
+
+ public void setCatalog(String catalog) throws SQLException {
+ getDelegate().setCatalog(catalog);
+ }
+
+ public String getCatalog() throws SQLException {
+ return getDelegate().getCatalog();
+ }
+
+ public void setTransactionIsolation(int level) throws SQLException {
+ getDelegate().setTransactionIsolation(level);
+ }
+
+ public int getTransactionIsolation() throws SQLException {
+ return getDelegate().getTransactionIsolation();
+ }
+
+ public SQLWarning getWarnings() throws SQLException {
+ return getDelegate().getWarnings();
+ }
+
+ public void clearWarnings() throws SQLException {
+ getDelegate().clearWarnings();
+ }
+
+ public Statement createStatement(int resultSetType,
+ int resultSetConcurrency) throws SQLException {
+ return getDelegate().createStatement(resultSetType, resultSetConcurrency);
+ }
+
+ public PreparedStatement prepareStatement(String sql, int resultSetType,
+ int resultSetConcurrency) throws SQLException {
+ return getDelegate().prepareStatement(sql, resultSetType,
+ resultSetConcurrency);
+ }
+
+ public CallableStatement prepareCall(String sql, int resultSetType,
+ int resultSetConcurrency) throws SQLException {
+ return getDelegate().prepareCall(sql, resultSetType, resultSetConcurrency);
+ }
+
+ public Map<String,Class< ? >> getTypeMap() throws SQLException {
+ return getDelegate().getTypeMap();
+ }
+
+ public void setTypeMap(Map<String,Class< ? >> map) throws SQLException {
+ getDelegate().setTypeMap(map);
+ }
+
+ public void setHoldability(int holdability) throws SQLException {
+ getDelegate().setHoldability(holdability);
+ }
+
+ public int getHoldability() throws SQLException {
+ return getDelegate().getHoldability();
+ }
+
+ public Savepoint setSavepoint() throws SQLException {
+ // This is a no-op
+ return null;
+ }
+
+ public Savepoint setSavepoint(String name) throws SQLException {
+ // This is a no-op
+ return null;
+ }
+
+ public void rollback(Savepoint savepoint) throws SQLException {
+ // This is a no-op
+ }
+
+ public void releaseSavepoint(Savepoint savepoint) throws SQLException {
+ // This is a no-op
+ }
+
+ public Statement createStatement(int resultSetType,
+ int resultSetConcurrency, int resultSetHoldability)
+ throws SQLException {
+ return getDelegate().createStatement(resultSetType, resultSetConcurrency,
+ resultSetHoldability);
+ }
+
+ public PreparedStatement prepareStatement(String sql, int resultSetType,
+ int resultSetConcurrency, int resultSetHoldability)
+ throws SQLException {
+ return getDelegate().prepareStatement(sql, resultSetType,
+ resultSetConcurrency, resultSetHoldability);
+ }
+
+ public CallableStatement prepareCall(String sql, int resultSetType,
+ int resultSetConcurrency, int resultSetHoldability)
+ throws SQLException {
+ return getDelegate().prepareCall(sql, resultSetType, resultSetConcurrency,
+ resultSetHoldability);
+ }
+
+ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
+ throws SQLException {
+ return getDelegate().prepareStatement(sql, autoGeneratedKeys);
+ }
+
+ public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
+ throws SQLException {
+ return getDelegate().prepareStatement(sql, columnIndexes);
+ }
+
+ public PreparedStatement prepareStatement(String sql, String[] columnNames)
+ throws SQLException {
+ return getDelegate().prepareStatement(sql, columnNames);
+ }
+
+ public Clob createClob() throws SQLException {
+ return getDelegate().createClob();
+ }
+
+ public Blob createBlob() throws SQLException {
+ return getDelegate().createBlob();
+ }
+
+ public NClob createNClob() throws SQLException {
+ return getDelegate().createNClob();
+ }
+
+ public SQLXML createSQLXML() throws SQLException {
+ return getDelegate().createSQLXML();
+ }
+
+ public boolean isValid(int timeout) throws SQLException {
+ return getDelegate().isValid(timeout);
+ }
+
+ public void setClientInfo(String name, String value)
+ throws SQLClientInfoException {
+ getDelegate().setClientInfo(name, value);
+ }
+
+ public void setClientInfo(Properties properties)
+ throws SQLClientInfoException {
+ getDelegate().setClientInfo(properties);
+ }
+
+ public String getClientInfo(String name) throws SQLException {
+ return getDelegate().getClientInfo(name);
+ }
+
+ public Properties getClientInfo() throws SQLException {
+ return getDelegate().getClientInfo();
+ }
+
+ public Array createArrayOf(String typeName, Object[] elements)
+ throws SQLException {
+ return getDelegate().createArrayOf(typeName, elements);
+ }
+
+ public Struct createStruct(String typeName, Object[] attributes)
+ throws SQLException {
+ return getDelegate().createStruct(typeName, attributes);
+ }
+
+ public void setSchema(String schema) throws SQLException {
+ getDelegate().setSchema(schema);
+ }
+
+ public String getSchema() throws SQLException {
+ return getDelegate().getSchema();
+ }
+
+ public void abort(Executor executor) throws SQLException {
+ getDelegate().abort(executor);
+ }
+
+ public void setNetworkTimeout(Executor executor, int milliseconds)
+ throws SQLException {
+ getDelegate().setNetworkTimeout(executor, milliseconds);
+ }
+
+ public int getNetworkTimeout() throws SQLException {
+ return getDelegate().getNetworkTimeout();
+ }
+
+ protected Connection getDelegate() {
+ return connection;
+ }
+}
\ No newline at end of file
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingJDBCDelegatingEntityManager.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingJDBCDelegatingEntityManager.java
new file mode 100644
index 0000000..aac306d
--- /dev/null
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingJDBCDelegatingEntityManager.java
@@ -0,0 +1,111 @@
+/*
+ * 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.transaction.control.TransactionStatus.NO_TRANSACTION;
+import static org.osgi.service.transaction.control.TransactionStatus.ROLLED_BACK;
+
+import java.util.UUID;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
+import org.apache.aries.tx.control.jpa.common.impl.EntityManagerWrapper;
+import org.apache.aries.tx.control.jpa.common.impl.ScopedEntityManagerWrapper;
+import org.apache.aries.tx.control.jpa.common.impl.TxEntityManagerWrapper;
+import org.osgi.service.transaction.control.TransactionContext;
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.TransactionException;
+
+public class TxContextBindingJDBCDelegatingEntityManager extends EntityManagerWrapper {
+
+ private final TransactionControl txControl;
+ private final UUID resourceId;
+ private final AbstractJPAEntityManagerProvider provider;
+
+ public TxContextBindingJDBCDelegatingEntityManager(TransactionControl txControl,
+ AbstractJPAEntityManagerProvider provider, UUID resourceId) {
+ this.txControl = txControl;
+ this.provider = provider;
+ this.resourceId = resourceId;
+ }
+
+ @Override
+ protected final EntityManager getRealEntityManager() {
+
+ TransactionContext txContext = txControl.getCurrentContext();
+
+ if (txContext == null) {
+ throw new TransactionException("The resource " + provider
+ + " cannot be accessed outside of an active Transaction Context");
+ }
+
+ EntityManager existing = (EntityManager) txContext.getScopedValue(resourceId);
+
+ if (existing != null) {
+ return existing;
+ }
+
+ EntityManager toReturn;
+ EntityManager toClose;
+
+ try {
+ if (txContext.getTransactionStatus() == NO_TRANSACTION) {
+ toClose = provider.createEntityManager();
+ toReturn = new ScopedEntityManagerWrapper(toClose);
+ } else {
+ toClose = provider.createEntityManager();
+ toReturn = new TxEntityManagerWrapper(toClose);
+
+ txContext.preCompletion(toClose::flush);
+ toClose.getTransaction().begin();
+ }
+ } catch (Exception sqle) {
+ throw new TransactionException(
+ "There was a problem getting hold of a database connection",
+ sqle);
+ }
+
+ txContext.postCompletion(s -> {
+ try {
+ // Make sure that the transaction ends,
+ // and that the EntityManager gets the
+ // right cache invalidation based on
+ // commit/rollback
+ if(s == ROLLED_BACK) {
+ toClose.getTransaction().rollback();
+ } else {
+ toClose.getTransaction().commit();
+ }
+ } catch (PersistenceException sqle) {
+ // TODO log this
+ }
+ try {
+ toClose.close();
+ } catch (PersistenceException sqle) {
+ // TODO log this
+ }
+ });
+
+ txContext.putScopedValue(resourceId, toReturn);
+
+ return toReturn;
+ }
+}
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
index e99edb4..406cd96 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
@@ -82,7 +82,7 @@ public class TxContextBindingEntityManagerTest {
Mockito.when(context.getScopedValue(Mockito.any()))
.thenAnswer(i -> variables.get(i.getArguments()[0]));
- provider = new JPAEntityManagerProviderImpl(emf, null);
+ provider = new JPAEntityManagerProviderImpl(emf, false, null);
em = new TxContextBindingEntityManager(control, provider, id);
}
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingJDBCDelegatingEntityManagerTest.java
similarity index 74%
copy from tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
copy to tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingJDBCDelegatingEntityManagerTest.java
index e99edb4..1570af2 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingJDBCDelegatingEntityManagerTest.java
@@ -21,12 +21,15 @@ package org.apache.aries.tx.control.jpa.local.impl;
import static org.mockito.Mockito.times;
import static org.osgi.service.transaction.control.TransactionStatus.ACTIVE;
+import static org.osgi.service.transaction.control.TransactionStatus.COMMITTED;
import static org.osgi.service.transaction.control.TransactionStatus.NO_TRANSACTION;
+import static org.osgi.service.transaction.control.TransactionStatus.ROLLED_BACK;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import java.util.function.Consumer;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
@@ -40,13 +43,13 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
-import org.osgi.service.transaction.control.LocalResource;
import org.osgi.service.transaction.control.TransactionContext;
import org.osgi.service.transaction.control.TransactionControl;
import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.TransactionStatus;
@RunWith(MockitoJUnitRunner.class)
-public class TxContextBindingEntityManagerTest {
+public class TxContextBindingJDBCDelegatingEntityManagerTest {
@Mock
TransactionControl control;
@@ -69,7 +72,7 @@ public class TxContextBindingEntityManagerTest {
AbstractJPAEntityManagerProvider provider;
- TxContextBindingEntityManager em;
+ TxContextBindingJDBCDelegatingEntityManager em;
@Before
public void setUp() throws SQLException {
@@ -82,9 +85,9 @@ public class TxContextBindingEntityManagerTest {
Mockito.when(context.getScopedValue(Mockito.any()))
.thenAnswer(i -> variables.get(i.getArguments()[0]));
- provider = new JPAEntityManagerProviderImpl(emf, null);
+ provider = new JPAEntityManagerProviderImpl(emf, true, null);
- em = new TxContextBindingEntityManager(control, provider, id);
+ em = new TxContextBindingJDBCDelegatingEntityManager(control, provider, id);
}
private void setupNoTransaction() {
@@ -118,6 +121,7 @@ public class TxContextBindingEntityManagerTest {
Mockito.verify(context).postCompletion(Mockito.any());
}
+ @SuppressWarnings("unchecked")
@Test
public void testActiveTransactionCommit() throws SQLException {
setupActiveTransaction();
@@ -125,22 +129,28 @@ public class TxContextBindingEntityManagerTest {
em.isOpen();
em.isOpen();
- ArgumentCaptor<LocalResource> captor = ArgumentCaptor.forClass(LocalResource.class);
-
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ @SuppressWarnings("rawtypes")
+ ArgumentCaptor captor2 = ArgumentCaptor.forClass(Consumer.class);
+
Mockito.verify(rawEm, times(2)).isOpen();
Mockito.verify(et).begin();
Mockito.verify(et, times(0)).commit();
Mockito.verify(et, times(0)).rollback();
- Mockito.verify(context).registerLocalResource(captor.capture());
+ Mockito.verify(context).preCompletion(captor.capture());
- Mockito.verify(context).postCompletion(Mockito.any());
+ Mockito.verify(context).postCompletion((Consumer<TransactionStatus>) captor2.capture());
- captor.getValue().commit();
+ captor.getValue().run();
+ Mockito.verify(rawEm).flush();
+
+ ((Consumer<TransactionStatus>)captor2.getValue()).accept(COMMITTED);
Mockito.verify(et).commit();
Mockito.verify(et, times(0)).rollback();
}
+ @SuppressWarnings("unchecked")
@Test
public void testActiveTransactionRollback() throws SQLException {
setupActiveTransaction();
@@ -148,31 +158,28 @@ public class TxContextBindingEntityManagerTest {
em.isOpen();
em.isOpen();
- ArgumentCaptor<LocalResource> captor = ArgumentCaptor.forClass(LocalResource.class);
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ @SuppressWarnings("rawtypes")
+ ArgumentCaptor captor2 = ArgumentCaptor.forClass(Consumer.class);
Mockito.verify(rawEm, times(2)).isOpen();
Mockito.verify(et).begin();
Mockito.verify(et, times(0)).commit();
Mockito.verify(et, times(0)).rollback();
- Mockito.verify(context).registerLocalResource(captor.capture());
+ Mockito.verify(context).preCompletion(captor.capture());
- Mockito.verify(context).postCompletion(Mockito.any());
+ Mockito.verify(context).postCompletion((Consumer<TransactionStatus>) captor2.capture());
+
+ captor.getValue().run();
+ Mockito.verify(rawEm).flush();
- captor.getValue().rollback();
+ ((Consumer<TransactionStatus>)captor2.getValue()).accept(ROLLED_BACK);
Mockito.verify(et).rollback();
Mockito.verify(et, times(0)).commit();
}
@Test(expected=TransactionException.class)
- public void testActiveTransactionNoLocal() throws SQLException {
- setupActiveTransaction();
-
- Mockito.when(context.supportsLocal()).thenReturn(false);
- em.isOpen();
- }
-
- @Test(expected=TransactionException.class)
public void testClosedProvider() throws SQLException {
setupActiveTransaction();
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 218e66f..51c112c 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
@@ -55,8 +55,10 @@ import org.apache.aries.tx.control.jdbc.common.impl.TxConnectionWrapper;
import org.apache.aries.tx.control.jdbc.xa.connection.impl.XAConnectionWrapper;
import org.apache.aries.tx.control.jdbc.xa.connection.impl.XADataSourceMapper;
import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
+import org.apache.aries.tx.control.jpa.common.impl.DelayedJPAEntityManagerProvider;
import org.apache.aries.tx.control.jpa.common.impl.InternalJPAEntityManagerProviderFactory;
import org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper;
+import org.apache.aries.tx.control.jpa.common.impl.ScopedConnectionDataSource;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.wiring.BundleWire;
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
index 4b1a1b5..20686d2 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPAEMFLocator.java
@@ -23,6 +23,7 @@ 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.apache.aries.tx.control.jpa.common.impl.DelayedJPAEntityManagerProvider;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
--
To stop receiving notification emails like this one, please contact
timothyjward@apache.org.