[tx-control] Update the JPA resource provider to support plugins when using the EntityManagerFactoryBuilder
git-svn-id: https://svn.apache.org/repos/asf/aries/trunk/tx-control@1809330 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
index 3bab3b4..34a911f 100644
--- a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
+++ b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
@@ -27,14 +27,20 @@
import java.io.File;
import java.io.IOException;
+import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Dictionary;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Properties;
import javax.inject.Inject;
import javax.persistence.EntityManager;
+import javax.sql.CommonDataSource;
import org.h2.tools.Server;
import org.junit.After;
@@ -58,6 +64,7 @@
import org.osgi.service.jpa.EntityManagerFactoryBuilder;
import org.osgi.service.transaction.control.TransactionControl;
import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
import org.osgi.util.tracker.ServiceTracker;
@RunWith(PaxExam.class)
@@ -67,18 +74,24 @@
protected static final String TX_CONTROL_FILTER = "tx.control.filter";
protected static final String ARIES_EMF_BUILDER_TARGET_FILTER = "aries.emf.builder.target.filter";
protected static final String IS_XA = "aries.test.is.xa";
+ protected static final String CONFIGURED_PROVIDER_PROPERTY = "org.apache.aries.tx.control.itests.configured";
@Inject
BundleContext context;
protected TransactionControl txControl;
+ protected JPAEntityManagerProvider provider;
protected EntityManager em;
+
+ // Set when using programmatic creation
+ protected EntityManagerFactoryBuilder builder;
+ protected Map<String, Object> jpaProps;
+ protected Map<String, Object> providerProps;
private Server server;
- private final List<ServiceTracker<?,?>> trackers = new ArrayList<>();
-
+ protected final List<ServiceTracker<?,?>> trackers = new ArrayList<>();
@Before
public void setUp() throws Exception {
@@ -89,9 +102,15 @@
String jdbcUrl = "jdbc:h2:tcp://127.0.0.1:" + server.getPort() + "/" + getRemoteDBPath();
- em = configuredEntityManager(jdbcUrl);
+ boolean configuredProvider = isConfigured();
+
+ em = configuredProvider ? configuredEntityManager(jdbcUrl) : programaticEntityManager(jdbcUrl);
}
+ public boolean isConfigured() {
+ return Boolean.getBoolean(CONFIGURED_PROVIDER_PROPERTY);
+ }
+
protected <T> T getService(Class<T> clazz, long timeout) {
try {
return getService(clazz, null, timeout);
@@ -161,7 +180,34 @@
return getService(JPAEntityManagerProvider.class, 5000).getResource(txControl);
}
+
+ private EntityManager programaticEntityManager(String jdbcURL) throws SQLException {
+
+ JPAEntityManagerProviderFactory resourceProviderFactory = getService(JPAEntityManagerProviderFactory.class, 5000);
+
+ DataSourceFactory dsf = getService(DataSourceFactory.class, 5000);
+
+ Properties props = new Properties();
+ props.put(DataSourceFactory.JDBC_URL, jdbcURL);
+ CommonDataSource dataSource = getBoolean(IS_XA) ? dsf.createXADataSource(props) :
+ dsf.createDataSource(props);
+
+ providerProps = new HashMap<>();
+ Dictionary<String,Object> baseProperties = getBaseProperties();
+ for (String string : Collections.list(baseProperties.keys())) {
+ providerProps.put(string, baseProperties.get(string));
+ }
+ jpaProps = new HashMap<>(providerProps);
+ jpaProps.put("javax.persistence.dataSource", dataSource);
+
+
+ builder = getService(EntityManagerFactoryBuilder.class, 5000);
+
+ provider = resourceProviderFactory.getProviderFor(builder, jpaProps, providerProps);
+ return provider.getResource(txControl);
+ }
+
protected Dictionary<String, Object> getBaseProperties() {
return new Hashtable<>();
}
@@ -176,8 +222,14 @@
}
trackers.stream().forEach(ServiceTracker::close);
+ trackers.clear();
+ txControl = null;
+ provider = null;
em = null;
+ builder = null;
+ jpaProps = null;
+ providerProps = null;
}
private void clearConfiguration() {
@@ -213,6 +265,51 @@
}
@Configuration
+ public Option[] localTxFactory() {
+ String localRepo = System.getProperty("maven.repo.local");
+ if (localRepo == null) {
+ localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository");
+ }
+
+ return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ when(localRepo != null)
+ .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)),
+ localTxControlService(),
+ localJpaResourceProviderWithH2(),
+ jpaProvider(),
+ ariesJPA(),
+ mavenBundle("org.apache.felix", "org.apache.felix.configadmin").versionAsInProject(),
+ mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(),
+ mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject()
+
+// ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+ );
+ }
+
+ @Configuration
+ public Option[] xaTxFactory() {
+ String localRepo = System.getProperty("maven.repo.local");
+ if (localRepo == null) {
+ localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository");
+ }
+
+ return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ when(localRepo != null)
+ .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)),
+ systemProperty(IS_XA).value(Boolean.TRUE.toString()),
+ xaTxControlService(),
+ xaJpaResourceProviderWithH2(),
+ jpaProvider(),
+ ariesJPA(),
+ mavenBundle("org.apache.felix", "org.apache.felix.configadmin").versionAsInProject(),
+ mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(),
+ mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject()
+
+// ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+ );
+ }
+
+ @Configuration
public Option[] localTxConfiguration() {
String localRepo = System.getProperty("maven.repo.local");
if (localRepo == null) {
@@ -222,6 +319,7 @@
return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
when(localRepo != null)
.useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)),
+ systemProperty(CONFIGURED_PROVIDER_PROPERTY).value(Boolean.TRUE.toString()),
localTxControlService(),
localJpaResourceProviderWithH2(),
jpaProvider(),
@@ -245,6 +343,7 @@
when(localRepo != null)
.useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)),
systemProperty(IS_XA).value(Boolean.TRUE.toString()),
+ systemProperty(CONFIGURED_PROVIDER_PROPERTY).value(Boolean.TRUE.toString()),
xaTxControlService(),
xaJpaResourceProviderWithH2(),
jpaProvider(),
diff --git a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractSimpleTransactionTest.java b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractSimpleTransactionTest.java
index 4653c9d..29187ae 100644
--- a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractSimpleTransactionTest.java
+++ b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractSimpleTransactionTest.java
@@ -28,11 +28,11 @@
import javax.persistence.criteria.CriteriaQuery;
import org.apache.aries.tx.control.itests.entity.Message;
-import org.junit.Assume;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.service.transaction.control.TransactionControl;
import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
public abstract class AbstractSimpleTransactionTest extends AbstractJPATransactionTest {
@@ -242,9 +242,8 @@
public void reassignTxControl() throws Exception {
Optional<Bundle> bundle = stream(context.getBundles())
- .filter(b -> b.getSymbolicName().equals("tx-control-provider-jpa-xa"))
+ .filter(b -> b.getSymbolicName().startsWith("tx-control-provider-jpa"))
.findAny();
- Assume.assumeTrue(bundle.isPresent());
bundle.get().stop();
bundle.get().start();
@@ -252,7 +251,12 @@
txControl = getService(TransactionControl.class,
System.getProperty(TX_CONTROL_FILTER), 5000);
- em = getService(JPAEntityManagerProvider.class, 5000).getResource(txControl);
+ if(isConfigured()) {
+ em = getService(JPAEntityManagerProvider.class, 5000).getResource(txControl);
+ } else {
+ em = getService(JPAEntityManagerProviderFactory.class, 5000)
+ .getProviderFor(builder, jpaProps, providerProps).getResource(txControl);
+ }
testTx();
}
diff --git a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
index 7ddd8f5..23b825e 100644
--- a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
+++ b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
@@ -35,6 +35,7 @@
import java.util.function.Predicate;
import org.apache.aries.tx.control.itests.entity.Message;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
@@ -49,7 +50,7 @@
import org.osgi.service.transaction.control.ScopedWorkException;
import org.osgi.service.transaction.control.TransactionException;
import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;
-import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerMethod.class)
@@ -146,12 +147,13 @@
} catch (BundleException e) {
}
});
- getService(JPAEntityManagerProvider.class, 5000);
+ getService(JPAEntityManagerProviderFactory.class, 5000);
}
}
@Test
public void testDeleteOfConfig() throws Exception {
+ Assume.assumeTrue("Not a config test", isConfigured());
Message m = new Message();
m.message = "Hello World";
@@ -183,6 +185,8 @@
@Test
public void testUpdateOfConfig() throws Exception {
+ Assume.assumeTrue("Not a config test", isConfigured());
+
Message m = new Message();
m.message = "Hello World";
txControl.required(() -> {em.persist(m); return null;});
@@ -209,34 +213,34 @@
assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());
}
}
-//
-// @Test
-// public void testReleaseOfFactoryService() {
-// Assume.assumeFalse("Not a factory test", isConfigured());
-//
-// txControl.required(
-// () -> connection.createStatement().execute("Insert into TEST_TABLE values ( 'Hello World!' )"));
-//
-// assertEquals("Hello World!", txControl.notSupported(() -> {
-// ResultSet rs = connection.createStatement().executeQuery("Select * from TEST_TABLE");
-// rs.next();
-// return rs.getString(1);
-// }));
-//
-// trackers.stream().filter(t -> t.getService() instanceof JDBCConnectionProviderFactory).findFirst().get()
-// .close();
-// ;
-//
-// try {
-// assertEquals("Hello World!", txControl.notSupported(() -> {
-// ResultSet rs = connection.createStatement().executeQuery("Select * from TEST_TABLE");
-// rs.next();
-// return rs.getString(1);
-// }));
-// fail("Should not be accessible");
-// } catch (ScopedWorkException swe) {
-// assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);
-// assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());
-// }
-// }
+
+ @Test
+ public void testReleaseOfFactoryService() {
+ Assume.assumeFalse("Not a factory test", isConfigured());
+
+ Message m = new Message();
+ m.message = "Hello World!";
+
+ txControl.required(
+ () -> { em.persist(m); return null;});
+
+ assertEquals("Hello World!", txControl.notSupported(() -> em.find(Message.class, m.id).message));
+
+ JPAEntityManagerProviderFactory factory = trackers.stream()
+ .map(t -> t.getService())
+ .filter(s -> s instanceof JPAEntityManagerProviderFactory)
+ .map(s -> (JPAEntityManagerProviderFactory) s)
+ .findFirst()
+ .get();
+
+ factory.releaseProvider(provider);
+
+ try {
+ assertEquals("Hello World!", txControl.notSupported(() -> em.find(Message.class, m.id).message));
+ fail("Should not be accessible");
+ } catch (ScopedWorkException swe) {
+ assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);
+ assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());
+ }
+ }
}
diff --git a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/XAJPATransactionTest.java b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/XAJPATransactionTest.java
index b800ff7..e9fe65c 100644
--- a/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/XAJPATransactionTest.java
+++ b/tx-control-providers/jpa/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/XAJPATransactionTest.java
@@ -18,8 +18,11 @@
*/
package org.apache.aries.tx.control.itests;
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.composite;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
@@ -32,15 +35,20 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Dictionary;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Properties;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
+import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
@@ -71,6 +79,7 @@
import org.osgi.service.transaction.control.TransactionControl;
import org.osgi.service.transaction.control.TransactionRolledBackException;
import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
import org.osgi.util.promise.Deferred;
import org.osgi.util.tracker.ServiceTracker;
@@ -82,6 +91,7 @@
static final String XA_TEST_UNIT_2 = "xa-test-unit-2";
protected static final String ARIES_EMF_BUILDER_TARGET_FILTER = "aries.emf.builder.target.filter";
+ protected static final String CONFIGURED_PROVIDER_PROPERTY = "org.apache.aries.tx.control.itests.configured";
@Inject
BundleContext context;
@@ -109,8 +119,14 @@
String jdbcUrl1 = "jdbc:h2:tcp://127.0.0.1:" + server1.getPort() + "/" + getRemoteDBPath("db1");
String jdbcUrl2 = "jdbc:h2:tcp://127.0.0.1:" + server2.getPort() + "/" + getRemoteDBPath("db2");
- em1 = configuredEntityManager(jdbcUrl1, XA_TEST_UNIT_1);
- em2 = configuredEntityManager(jdbcUrl2, XA_TEST_UNIT_2);
+ em1 = isConfigured() ? configuredEntityManager(jdbcUrl1, XA_TEST_UNIT_1) :
+ factoryEntityManager(jdbcUrl1, XA_TEST_UNIT_1);
+ em2 = isConfigured() ? configuredEntityManager(jdbcUrl2, XA_TEST_UNIT_2) :
+ factoryEntityManager(jdbcUrl2, XA_TEST_UNIT_2);
+ }
+
+ public boolean isConfigured() {
+ return Boolean.getBoolean(CONFIGURED_PROVIDER_PROPERTY);
}
private String getRemoteDBPath(String dbName) {
@@ -148,6 +164,32 @@
5000).getResource(txControl);
}
+ private EntityManager factoryEntityManager(String jdbcUrl, String unit) throws Exception {
+
+ JPAEntityManagerProviderFactory resourceProviderFactory = getService(JPAEntityManagerProviderFactory.class, 5000);
+
+ DataSourceFactory dsf = getService(DataSourceFactory.class, 5000);
+
+ Properties props = new Properties();
+ props.put(DataSourceFactory.JDBC_URL, jdbcUrl);
+ XADataSource dataSource = dsf.createXADataSource(props);
+
+ Map<String, Object> providerProps = new HashMap<>();
+ Dictionary<String,Object> baseProperties = getBaseProperties();
+ for (String string : Collections.list(baseProperties.keys())) {
+ providerProps.put(string, baseProperties.get(string));
+ }
+
+ providerProps.put("javax.persistence.dataSource", dataSource);
+
+
+ EntityManagerFactoryBuilder builder = getService(EntityManagerFactoryBuilder.class,
+ "(osgi.unit.name=" + unit + ")",5000);
+
+ return resourceProviderFactory.getProviderFor(builder, providerProps, providerProps)
+ .getResource(txControl);
+ }
+
private <T> T getService(Class<T> clazz, long timeout) {
try {
return getService(clazz, null, timeout);
@@ -197,6 +239,7 @@
}
trackers.stream().forEach(ServiceTracker::close);
+ trackers.clear();
em1 = null;
em2 = null;
@@ -234,6 +277,21 @@
@Configuration
public Option[] xaTxConfiguration() {
+ return options(
+ composite(xaTx()),
+ systemProperty(CONFIGURED_PROVIDER_PROPERTY).value(TRUE.toString())
+ );
+ }
+
+ @Configuration
+ public Option[] xaTxFactory() {
+ return options(
+ composite(xaTx()),
+ systemProperty(CONFIGURED_PROVIDER_PROPERTY).value(FALSE.toString())
+ );
+ }
+
+ private Option[] xaTx() {
String localRepo = System.getProperty("maven.repo.local");
if (localRepo == null) {
localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository");
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
index 76fae7c..1324a39 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractManagedJPADataSourceSetup.java
@@ -19,27 +19,15 @@
package org.apache.aries.tx.control.jpa.common.impl;
import static java.util.Collections.unmodifiableMap;
-import static java.util.Optional.ofNullable;
-import static java.util.concurrent.TimeUnit.HOURS;
-import static java.util.concurrent.TimeUnit.SECONDS;
import static org.osgi.framework.Constants.OBJECTCLASS;
import static org.osgi.service.jdbc.DataSourceFactory.OSGI_JDBC_DRIVER_CLASS;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_LIFETIME;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_POOLING_ENABLED;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_TIMEOUT;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.IDLE_TIMEOUT;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MAX_CONNECTIONS;
-import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MIN_CONNECTIONS;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
-import javax.sql.DataSource;
-
import org.apache.aries.tx.control.resource.common.impl.LifecycleAware;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
@@ -51,17 +39,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.zaxxer.hikari.HikariConfig;
-import com.zaxxer.hikari.HikariDataSource;
-
public abstract class AbstractManagedJPADataSourceSetup implements LifecycleAware,
ServiceTrackerCustomizer<DataSourceFactory, AbstractManagedJPAEMFLocator> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractManagedJPADataSourceSetup.class);
- // TODO - where should this go?
- private static final String CONNECTION_TEST_QUERY = "aries.connection.test.query";
-
private final BundleContext context;
private final String pid;
private final Properties jdbcProperties;
@@ -163,75 +145,4 @@
}
}
}
-
- protected DataSource poolIfNecessary(Map<String, Object> resourceProviderProperties, DataSource unpooled) {
- DataSource toUse;
-
- if (toBoolean(resourceProviderProperties, CONNECTION_POOLING_ENABLED, true)) {
- HikariConfig hcfg = new HikariConfig();
- hcfg.setDataSource(unpooled);
-
- // Sizes
- hcfg.setMaximumPoolSize(toInt(resourceProviderProperties, MAX_CONNECTIONS, 10));
- hcfg.setMinimumIdle(toInt(resourceProviderProperties, MIN_CONNECTIONS, 10));
-
- // Timeouts
- hcfg.setConnectionTimeout(toLong(resourceProviderProperties, CONNECTION_TIMEOUT, SECONDS.toMillis(30)));
- hcfg.setIdleTimeout(toLong(resourceProviderProperties, IDLE_TIMEOUT, TimeUnit.MINUTES.toMillis(3)));
- hcfg.setMaxLifetime(toLong(resourceProviderProperties, CONNECTION_LIFETIME, HOURS.toMillis(3)));
-
- hcfg.setConnectionTestQuery((String)resourceProviderProperties.get(CONNECTION_TEST_QUERY));
-
- toUse = new HikariDataSource(hcfg);
-
- } else {
- toUse = unpooled;
- }
- return toUse;
- }
-
- protected boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
- Object o = ofNullable(props)
- .map(m -> m.get(key))
- .orElse(defaultValue);
-
- if (o instanceof Boolean) {
- return ((Boolean) o).booleanValue();
- } else if(o instanceof String) {
- return Boolean.parseBoolean((String) o);
- } else {
- throw new IllegalArgumentException("The property " + key + " cannot be converted to a boolean");
- }
- }
-
- protected int toInt(Map<String, Object> props, String key, int defaultValue) {
-
- Object o = ofNullable(props)
- .map(m -> m.get(key))
- .orElse(defaultValue);
-
- if (o instanceof Number) {
- return ((Number) o).intValue();
- } else if(o instanceof String) {
- return Integer.parseInt((String) o);
- } else {
- throw new IllegalArgumentException("The property " + key + " cannot be converted to an int");
- }
- }
-
- private long toLong(Map<String, Object> props, String key, long defaultValue) {
-
- Object o = ofNullable(props)
- .map(m -> m.get(key))
- .orElse(defaultValue);
-
- if (o instanceof Number) {
- return ((Number) o).longValue();
- } else if(o instanceof String) {
- return Long.parseLong((String) o);
- } else {
- throw new IllegalArgumentException("The property " + key + " cannot be converted to a long");
- }
- }
-
}
\ No newline at end of file
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPADataSourceHelper.java b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPADataSourceHelper.java
new file mode 100644
index 0000000..d5e335b
--- /dev/null
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-common/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPADataSourceHelper.java
@@ -0,0 +1,94 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import static java.util.Optional.ofNullable;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_LIFETIME;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_POOLING_ENABLED;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_TIMEOUT;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.IDLE_TIMEOUT;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MAX_CONNECTIONS;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MIN_CONNECTIONS;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.sql.DataSource;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+public class JPADataSourceHelper {
+
+ public static final String CONNECTION_TEST_QUERY = "aries.connection.test.query";
+
+ public static DataSource poolIfNecessary(Map<String, Object> resourceProviderProperties, DataSource unpooled) {
+ DataSource toUse;
+
+ if (toBoolean(resourceProviderProperties, CONNECTION_POOLING_ENABLED, true)) {
+ HikariConfig hcfg = new HikariConfig();
+ hcfg.setDataSource(unpooled);
+
+ // Sizes
+ hcfg.setMaximumPoolSize(toInt(resourceProviderProperties, MAX_CONNECTIONS, 10));
+ hcfg.setMinimumIdle(toInt(resourceProviderProperties, MIN_CONNECTIONS, 10));
+
+ // Timeouts
+ hcfg.setConnectionTimeout(toLong(resourceProviderProperties, CONNECTION_TIMEOUT, SECONDS.toMillis(30)));
+ hcfg.setIdleTimeout(toLong(resourceProviderProperties, IDLE_TIMEOUT, TimeUnit.MINUTES.toMillis(3)));
+ hcfg.setMaxLifetime(toLong(resourceProviderProperties, CONNECTION_LIFETIME, HOURS.toMillis(3)));
+
+ hcfg.setConnectionTestQuery((String)resourceProviderProperties.get(CONNECTION_TEST_QUERY));
+
+ toUse = new HikariDataSource(hcfg);
+
+ } else {
+ toUse = unpooled;
+ }
+ return toUse;
+ }
+
+ public static boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
+ Object o = ofNullable(props)
+ .map(m -> m.get(key))
+ .orElse(defaultValue);
+
+ if (o instanceof Boolean) {
+ return ((Boolean) o).booleanValue();
+ } else if(o instanceof String) {
+ return Boolean.parseBoolean((String) o);
+ } else {
+ throw new IllegalArgumentException("The property " + key + " cannot be converted to a boolean");
+ }
+ }
+
+ public static int toInt(Map<String, Object> props, String key, int defaultValue) {
+
+ Object o = ofNullable(props)
+ .map(m -> m.get(key))
+ .orElse(defaultValue);
+
+ if (o instanceof Number) {
+ return ((Number) o).intValue();
+ } else if(o instanceof String) {
+ return Integer.parseInt((String) o);
+ } else {
+ throw new IllegalArgumentException("The property " + key + " cannot be converted to an int");
+ }
+ }
+
+ public static long toLong(Map<String, Object> props, String key, long defaultValue) {
+
+ Object o = ofNullable(props)
+ .map(m -> m.get(key))
+ .orElse(defaultValue);
+
+ if (o instanceof Number) {
+ return ((Number) o).longValue();
+ } else if(o instanceof String) {
+ return Long.parseLong((String) o);
+ } else {
+ throw new IllegalArgumentException("The property " + key + " cannot be converted to a long");
+ }
+ }
+}
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 fd5f4aa..4f6673a 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
@@ -23,16 +23,21 @@
import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.LOCAL_ENLISTMENT_ENABLED;
import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.XA_ENLISTMENT_ENABLED;
+import java.util.HashMap;
import java.util.Map;
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.InternalJPAEntityManagerProviderFactory;
+import org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper;
import org.osgi.service.jpa.EntityManagerFactoryBuilder;
import org.osgi.service.transaction.control.TransactionException;
+import com.zaxxer.hikari.HikariDataSource;
+
public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityManagerProviderFactory {
@Override
@@ -40,11 +45,38 @@
Map<String, Object> resourceProviderProperties) {
checkEnlistment(resourceProviderProperties);
- EntityManagerFactory emf = emfb.createEntityManagerFactory(jpaProperties);
+ Object found = jpaProperties.get("javax.persistence.dataSource");
+
+ if(found == null) {
+ found = jpaProperties.get("javax.persistence.nonJtaDataSource");
+ }
+
+ if(found == null) {
+ throw new IllegalArgumentException("No datasource was found when checking the javax.persistence.dataSource and javax.persistence.nonJtaDataSource.");
+ }
+
+ DataSource unpooled;
+ if(found instanceof DataSource) {
+ unpooled = (DataSource) found;
+ } else {
+ throw new IllegalArgumentException("The object found when checking the javax.persistence.dataSource and javax.persistence.nonJtaDataSource properties was not a DataSource.");
+ }
+
+ Map<String, Object> jpaPropsToUse = new HashMap<>(jpaProperties);
+ DataSource toUse = JPADataSourceHelper.poolIfNecessary(resourceProviderProperties, unpooled);
+ jpaPropsToUse.put("javax.persistence.dataSource", toUse);
+ jpaPropsToUse.put("javax.persistence.nonJtaDataSource", toUse);
+
+ EntityManagerFactory emf = emfb.createEntityManagerFactory(jpaPropsToUse);
validateEMF(emf);
- return new JPAEntityManagerProviderImpl(emf, () -> emf.close());
+ return new JPAEntityManagerProviderImpl(emf, () -> {
+ emf.close();
+ if (toUse instanceof HikariDataSource) {
+ ((HikariDataSource)toUse).close();
+ }
+ });
}
public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb,
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
index 94d8956..c77f38b 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LocalJPADataSourceSetup.java
@@ -18,6 +18,8 @@
*/
package org.apache.aries.tx.control.jpa.local.impl;
+import static org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper.poolIfNecessary;
+import static org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper.toBoolean;
import static org.osgi.service.jdbc.DataSourceFactory.JDBC_URL;
import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.USE_DRIVER;
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-parent/pom.xml b/tx-control-providers/jpa/tx-control-provider-jpa-parent/pom.xml
index e326b28..e6633a4 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-parent/pom.xml
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-parent/pom.xml
@@ -54,7 +54,7 @@
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.jpa</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
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 6146843..9686e7f 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
@@ -36,7 +36,7 @@
protected ResourceProviderFactoryServiceFactory<AbstractJPAEntityManagerProvider, ResourceTrackingJPAEntityManagerProviderFactory> getServiceFactory(
BundleContext context) {
- InternalJPAEntityManagerProviderFactory ijempf = new JPAEntityManagerProviderFactoryImpl();
+ InternalJPAEntityManagerProviderFactory ijempf = new JPAEntityManagerProviderFactoryImpl(context);
return new ResourceProviderFactoryServiceFactory<AbstractJPAEntityManagerProvider, ResourceTrackingJPAEntityManagerProviderFactory>() {
@Override
protected TrackingResourceProviderFactory<AbstractJPAEntityManagerProvider> getTrackingResourceManagerProviderFactory() {
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 25ddb28..c4ce8f9 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
@@ -20,42 +20,173 @@
import static java.util.Optional.ofNullable;
import static javax.persistence.spi.PersistenceUnitTransactionType.JTA;
+import static org.apache.aries.tx.control.jpa.xa.impl.XAJPADataSourceSetup.JTA_DATA_SOURCE;
+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.PRE_ENLISTED_DB_CONNECTION;
import static org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory.XA_ENLISTMENT_ENABLED;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Wrapper;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
-import java.util.logging.Logger;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
+import javax.sql.XADataSource;
import javax.transaction.xa.XAResource;
import org.apache.aries.tx.control.jdbc.common.impl.ScopedConnectionWrapper;
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.InternalJPAEntityManagerProviderFactory;
+import org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.jpa.EntityManagerFactoryBuilder;
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.jdbc.JDBCConnectionProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.zaxxer.hikari.HikariDataSource;
public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityManagerProviderFactory {
+ private static final String TRANSACTION_TYPE = "javax.persistence.transactionType";
+ private static final Logger LOGGER = LoggerFactory.getLogger(JPAEntityManagerProviderFactoryImpl.class);
+ private final BundleContext context;
+
+ public JPAEntityManagerProviderFactoryImpl(BundleContext context) {
+ this.context = context;
+ }
+
@Override
public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties,
Map<String, Object> resourceProviderProperties) {
- return new DelayedJPAEntityManagerProvider(tx -> getProviderFor(emfb, jpaProperties, resourceProviderProperties, tx, null));
+
+ Map<String, Object> jpaPropsToUse = new HashMap<>(jpaProperties);
+ jpaPropsToUse.put(TRANSACTION_TYPE, JTA.name());
+
+ Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> create;
+ if(jpaProperties.containsKey("osgi.jdbc.provider")) {
+ create = handleJDBCResourceProvider(emfb, resourceProviderProperties, jpaPropsToUse);
+ } else if(toBoolean(jpaPropsToUse, PRE_ENLISTED_DB_CONNECTION, false)) {
+ create = handlePreEnlistedConnection(emfb, resourceProviderProperties, jpaPropsToUse);
+ } else {
+ create = handleNormalDataSource(emfb, resourceProviderProperties, jpaPropsToUse);
+ }
+
+ return new DelayedJPAEntityManagerProvider(create);
+ }
+
+ private Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> handleJDBCResourceProvider(
+ EntityManagerFactoryBuilder emfb, Map<String, Object> resourceProviderProperties,
+ Map<String, Object> jpaPropsToUse) {
+ Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> create;
+ JDBCConnectionProvider provider = (JDBCConnectionProvider) jpaPropsToUse.get("osgi.jdbc.provider");
+
+ create = tx -> {
+ jpaPropsToUse.put(JTA_DATA_SOURCE,
+ new ScopedConnectionDataSource(provider.getResource(tx.get())));
+ jpaPropsToUse.put(PRE_ENLISTED_DB_CONNECTION, Boolean.TRUE);
+
+ return getProviderFor(emfb, jpaPropsToUse, resourceProviderProperties, tx, null);
+ };
+ return create;
+ }
+
+ private Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> handlePreEnlistedConnection(
+ EntityManagerFactoryBuilder emfb, Map<String, Object> resourceProviderProperties,
+ Map<String, Object> jpaPropsToUse) {
+ Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> create;
+ Object supplied = jpaPropsToUse.get(JTA_DATA_SOURCE);
+ if(supplied == null) {
+ 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");
+ }
+ create = tx -> {
+ DataSource toUse = JPADataSourceHelper.poolIfNecessary(resourceProviderProperties, (DataSource) supplied);
+ jpaPropsToUse.put(JTA_DATA_SOURCE, toUse);
+ return getProviderFor(emfb, jpaPropsToUse, resourceProviderProperties, tx, () -> {
+ if (toUse instanceof HikariDataSource) {
+ ((HikariDataSource)toUse).close();
+ }
+ });
+ };
+ return create;
+ }
+
+ private Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> handleNormalDataSource(
+ EntityManagerFactoryBuilder emfb, Map<String, Object> resourceProviderProperties,
+ Map<String, Object> jpaPropsToUse) {
+ Function<ThreadLocal<TransactionControl>, AbstractJPAEntityManagerProvider> create;
+ Object supplied = jpaPropsToUse.get(JTA_DATA_SOURCE);
+ if(supplied == null) {
+ supplied = jpaPropsToUse.get("javax.persistence.dataSource");
+ }
+ if(supplied == null) {
+ supplied = jpaPropsToUse.get(NON_JTA_DATA_SOURCE);
+ }
+
+ if(supplied == null) {
+ LOGGER.error("No datasource supplied in the configuration");
+ throw new IllegalArgumentException("No datasource could be found to create the EntityManagerFactory. Please provide either a javax.persistence.jtaDataSource, a javax.persistence.nonJtaDataSource, or a javax.persistence.dataSource");
+ }
+
+ DataSource ds;
+
+ try {
+ if (supplied instanceof XADataSource) {
+ ds = new XADataSourceMapper((XADataSource)supplied);
+ } else if (supplied instanceof Wrapper && ((Wrapper)supplied).isWrapperFor(XADataSource.class)) {
+ ds = new XADataSourceMapper(((Wrapper)supplied).unwrap(XADataSource.class));
+ } else {
+ LOGGER.error("The datasource supplied was not XA capable");
+ throw new IllegalArgumentException("The datasource supplied to create the EntityManagerFactory is not an XADataSource and so cannot be enlisted. Please provide either a javax.persistence.jtaDataSource, a javax.persistence.nonJtaDataSource, or a javax.persistence.dataSource which implements XADataSource");
+ }
+ } catch (SQLException sqle) {
+ LOGGER.error("Unable to obtain an XA DataSource for the JPAEntityManagerProvider", sqle);
+ throw new IllegalArgumentException("The supplied DataSource could not be enlisted with XA transactions", sqle);
+ }
+
+
+ create = tx -> {
+ DataSource toUse = JPADataSourceHelper.poolIfNecessary(resourceProviderProperties, ds);
+ jpaPropsToUse.put(JTA_DATA_SOURCE, toUse);
+ Object o = jpaPropsToUse.get(NON_JTA_DATA_SOURCE);
+ if(o == null) {
+ jpaPropsToUse.put(NON_JTA_DATA_SOURCE, toUse);
+ } else if (o instanceof DataSource) {
+ jpaPropsToUse.put(NON_JTA_DATA_SOURCE, JPADataSourceHelper
+ .poolIfNecessary(resourceProviderProperties, (DataSource) o));
+ }
+ return getProviderFor(emfb, jpaPropsToUse, resourceProviderProperties, tx, () -> {
+ if (toUse instanceof HikariDataSource) {
+ ((HikariDataSource)toUse).close();
+ }
+ });
+ };
+ return create;
}
public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties,
@@ -66,23 +197,157 @@
} else {
toUse = jpaProperties;
}
+
+ setupTransactionManager(context, toUse, localStore, emfb);
+
return localStore.get().notSupported(() -> internalBuilderCreate(emfb, toUse, localStore, onClose));
}
+ private boolean checkEnlistment(Map<String, Object> resourceProviderProperties) {
+ if (toBoolean(resourceProviderProperties, LOCAL_ENLISTMENT_ENABLED, false)) {
+ throw new TransactionException("This Resource Provider does not support Local transactions");
+ } else if (!toBoolean(resourceProviderProperties, XA_ENLISTMENT_ENABLED, true)) {
+ throw new TransactionException(
+ "This Resource Provider always enlists in XA transactions as it does not support local transactions");
+ }
+
+ return !toBoolean(resourceProviderProperties, PRE_ENLISTED_DB_CONNECTION, false);
+ }
+
private Map<String, Object> enlistDataSource(ThreadLocal<TransactionControl> tx, Map<String, Object> jpaProperties) {
Map<String, Object> toReturn = new HashMap<>(jpaProperties);
- DataSource ds = (DataSource) jpaProperties.get("javax.persistence.jtaDataSource");
-
- if(!jpaProperties.containsKey("javax.persistence.nonJtaDataSource")) {
- toReturn.put("javax.persistence.nonJtaDataSource", ds);
- }
+ DataSource enlistedDS = new EnlistingDataSource(tx,
+ (DataSource)jpaProperties.get(JTA_DATA_SOURCE));
- toReturn.put("javax.persistence.jtaDataSource", new EnlistingDataSource(tx, ds));
+ toReturn.put(JTA_DATA_SOURCE, enlistedDS);
return toReturn;
}
+ private void setupTransactionManager(BundleContext context, Map<String, Object> props,
+ ThreadLocal<TransactionControl> t, EntityManagerFactoryBuilder builder) {
+ String provider = builder.getPersistenceProviderName();
+ Bundle providerBundle = builder.getPersistenceProviderBundle();
+
+ if(providerBundle == null) {
+ LOGGER.warn("Unable to find a Persistence Provider for the provider named {}, so no XA plugin can be registered. XA transactions are unlikely to function properly.", provider);
+ return;
+ }
+
+ Bundle txControlProviderBundle = context.getBundle();
+
+ try {
+ if("org.hibernate.jpa.HibernatePersistenceProvider".equals(provider)) {
+
+ if(props.containsKey("hibernate.transaction.coordinator_class")) {
+ LOGGER.warn("The JPA configuration properties already define a Hibernate transaction coordinator. This resource provider will not install its own plugin.");
+ return;
+ }
+
+ String pluginClass;
+
+ Bundle toUse = findSource(providerBundle, "org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder");
+
+ if(toUse != null) {
+
+ try {
+ toUse.loadClass("org.hibernate.resource.transaction.spi.DdlTransactionIsolator");
+ LOGGER.debug("Detected Hibernate 5.2.2 or above when attempting to install the XA plugin.");
+ pluginClass = "org.apache.aries.tx.control.jpa.xa.plugin.hibernate.impl.Hibernate522TxControlPlatform";
+ } catch (Exception e) {
+ LOGGER.debug("Detected Hibernate 5.2.0 or 5.2.1 when attempting to install the XA plugin.");
+ pluginClass = "org.apache.aries.tx.control.jpa.xa.plugin.hibernate.impl.Hibernate520TxControlPlatform";
+ }
+ } else {
+ toUse = findSource(providerBundle, "org.hibernate.resource.transaction.TransactionCoordinatorBuilder");
+ if(toUse != null) {
+ LOGGER.debug("Detected Hibernate 5.0.x or 5.1.x or above when attempting to install the XA plugin.");
+ pluginClass = "org.apache.aries.tx.control.jpa.xa.plugin.hibernate.impl.HibernateTxControlPlatform";
+ } else {
+ LOGGER.warn("Detected a Hibernate provider, but we were unable to load an appropriate XA plugin");
+ return;
+ }
+ }
+
+ ClassLoader pluginLoader = getPluginLoader(toUse, txControlProviderBundle);
+
+ Class<?> pluginClazz = pluginLoader.loadClass(pluginClass);
+ Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
+ .newInstance(t);
+
+ props.put("hibernate.transaction.coordinator_class", plugin);
+
+ } else if("org.apache.openjpa.persistence.PersistenceProviderImpl".equals(provider)) {
+
+ if(props.containsKey("openjpa.ManagedRuntime")) {
+ LOGGER.warn("The JPA configuration properties already define an OpenJPA transaction runtime. This resource provider will not install its own plugin.");
+ return;
+ }
+
+ ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
+
+ Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.plugin.openjpa.impl.OpenJPATxControlPlatform");
+ Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
+ .newInstance(t);
+
+ props.put("openjpa.ManagedRuntime", plugin);
+ props.put("openjpa.ConnectionFactoryMode", "managed");
+ props.put("openjpa.TransactionMode", "managed");
+
+ } else if("org.eclipse.persistence.jpa.PersistenceProvider".equals(provider)) {
+
+ if(props.containsKey("eclipselink.target-server")) {
+ LOGGER.warn("The JPA configuration properties already define an EclipseLink transaction target. This resource provider will not install its own plugin.");
+ return;
+ }
+
+ ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
+
+ Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.plugin.eclipse.impl.EclipseTxControlPlatform");
+
+ pluginClazz.getMethod("setTransactionControl", ThreadLocal.class)
+ .invoke(null, t);
+
+ props.put("eclipselink.target-server", pluginClazz.getName());
+ props.put("org.apache.aries.jpa.eclipselink.plugin.types", pluginClazz);
+ // This is needed to ensure that sequences can be generated in nested
+ // transactions without blowing up.
+ if(!props.containsKey("eclipselink.jdbc.sequence-connection-pool")) {
+ props.put("eclipselink.jdbc.sequence-connection-pool", "true");
+ }
+
+ } else {
+ LOGGER.warn("The persistence provider {} is not recognised, so no adapter plugin can be registered with it. XA transactions are unlikely to work properly", provider);
+ return;
+ }
+ } catch (Exception e) {
+ LOGGER.error("There was a problem trying to install a transaction integration plugin for the JPA provider {}.", provider, e);
+ }
+ }
+
+ private Bundle findSource(Bundle providerBundle, String toFind) {
+ try{
+ providerBundle.loadClass(toFind);
+ return providerBundle;
+ } catch (Exception e) {
+ BundleWiring wiring = providerBundle.adapt(BundleWiring.class);
+ return wiring.getRequiredWires("osgi.wiring.package").stream()
+ .filter(bw -> "org.hibernate".equals(bw.getCapability().getAttributes().get("osgi.wiring.package")))
+ .map(BundleWire::getProviderWiring)
+ .map(BundleWiring::getBundle)
+ .findFirst()
+ .filter(b -> {
+ try {
+ b.loadClass(toFind);
+ return true;
+ } catch (Exception e2) {
+ return false;
+ }
+ }).orElse(null);
+ }
+ }
+
private AbstractJPAEntityManagerProvider internalBuilderCreate(EntityManagerFactoryBuilder emfb,
Map<String, Object> jpaProperties, ThreadLocal<TransactionControl> tx, Runnable onClose) {
EntityManagerFactory emf = emfb.createEntityManagerFactory(jpaProperties);
@@ -101,7 +366,7 @@
}
private void validateEMF(EntityManagerFactory emf) {
- Object o = emf.getProperties().get("javax.persistence.transactionType");
+ Object o = emf.getProperties().get(TRANSACTION_TYPE);
PersistenceUnitTransactionType tranType;
if(o instanceof PersistenceUnitTransactionType) {
@@ -114,7 +379,7 @@
}
if(JTA != tranType) {
- throw new IllegalArgumentException("The supplied EntityManagerFactory is not declared RESOURCE_LOCAL");
+ throw new IllegalArgumentException("The supplied EntityManagerFactory is not declared JTA");
}
}
@@ -127,17 +392,6 @@
return new JPAEntityManagerProviderImpl(emf, new ThreadLocal<>(), null);
}
- private boolean checkEnlistment(Map<String, Object> resourceProviderProperties) {
- if (toBoolean(resourceProviderProperties, LOCAL_ENLISTMENT_ENABLED, false)) {
- throw new TransactionException("This Resource Provider does not support Local transactions");
- } else if (!toBoolean(resourceProviderProperties, XA_ENLISTMENT_ENABLED, true)) {
- throw new TransactionException(
- "This Resource Provider always enlists in XA transactions as it does not support local transactions");
- }
-
- return !toBoolean(resourceProviderProperties, PRE_ENLISTED_DB_CONNECTION, false);
- }
-
public static boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
Object o = ofNullable(props)
.map(m -> m.get(key))
@@ -152,6 +406,50 @@
}
}
+ private ClassLoader getPluginLoader(Bundle providerBundle, Bundle txControlProviderBundle) {
+ return new ClassLoader() {
+
+ ConcurrentMap<String, Class<?>> loaded = new ConcurrentHashMap<>();
+
+ @Override
+ public Class<?> loadClass(String name) throws ClassNotFoundException {
+ if(name.startsWith("org.apache.aries.tx.control.jpa.xa.plugin")) {
+
+ Class<?> c = loaded.get(name);
+
+ if(c != null) {
+ return c;
+ }
+
+ String resource = name.replace('.', '/') + ".class";
+
+ try (InputStream is = txControlProviderBundle.getResource(resource).openStream()) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+ byte[] b = new byte[4096];
+ int read;
+ while ((read = is.read(b)) != -1) {
+ baos.write(b, 0, read);
+ }
+ byte[] clazzBytes = baos.toByteArray();
+ c = defineClass(name, clazzBytes, 0, clazzBytes.length,
+ XAJPAEMFLocator.class.getProtectionDomain());
+ loaded.putIfAbsent(name, c);
+ return c;
+ } catch (IOException e) {
+ throw new ClassNotFoundException("Unable to load class " + name, e);
+ }
+ }
+
+ if(name.startsWith("org.apache.aries.tx.control") ||
+ name.startsWith("org.osgi.service.transaction.control") ||
+ name.startsWith("org.slf4j")) {
+ return txControlProviderBundle.loadClass(name);
+ }
+ return providerBundle.loadClass(name);
+ }
+ };
+ }
+
public static class EnlistingDataSource implements DataSource {
private final DataSource delegate;
@@ -205,7 +503,7 @@
return delegate.getLoginTimeout();
}
- public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
return delegate.getParentLogger();
}
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-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ScopedConnectionDataSource.java
new file mode 100644
index 0000000..11fcfa5
--- /dev/null
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/ScopedConnectionDataSource.java
@@ -0,0 +1,70 @@
+package org.apache.aries.tx.control.jpa.xa.impl;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+public class ScopedConnectionDataSource implements DataSource {
+
+ private final Connection scoped;
+
+ public ScopedConnectionDataSource(Connection scoped) {
+ this.scoped = scoped;
+ }
+
+ @Override
+ public PrintWriter getLogWriter() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void setLogWriter(PrintWriter out) throws SQLException {
+ // A no-op
+
+ }
+
+ @Override
+ public void setLoginTimeout(int seconds) throws SQLException {
+ // A no-op
+ }
+
+ @Override
+ public int getLoginTimeout() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ return Logger.getGlobal();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ if (iface.isInstance(scoped)) {
+ return (T) scoped;
+ } else {
+ return scoped.unwrap(iface);
+ }
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ return iface.isInstance(scoped) || scoped.isWrapperFor(iface);
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ return scoped;
+ }
+
+ @Override
+ public Connection getConnection(String username, String password) throws SQLException {
+ return scoped;
+ }
+
+}
diff --git a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
index 876c0bc..e6cb6cf 100644
--- a/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
+++ b/tx-control-providers/jpa/tx-control-provider-jpa-xa/src/main/java/org/apache/aries/tx/control/jpa/xa/impl/XAJPADataSourceSetup.java
@@ -18,6 +18,8 @@
*/
package org.apache.aries.tx.control.jpa.xa.impl;
+import static org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper.poolIfNecessary;
+import static org.apache.aries.tx.control.jpa.common.impl.JPADataSourceHelper.toBoolean;
import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.USE_DRIVER;
import java.sql.SQLException;
@@ -39,7 +41,8 @@
public class XAJPADataSourceSetup extends AbstractManagedJPADataSourceSetup {
- private static final String JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE = "javax.persistence.nonJtaDataSource";
+ static final String JTA_DATA_SOURCE = "javax.persistence.jtaDataSource";
+ static final String NON_JTA_DATA_SOURCE = "javax.persistence.nonJtaDataSource";
public XAJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties,
Map<String, Object> baseJPAProperties, Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
@@ -62,14 +65,15 @@
DataSource toUse = poolIfNecessary(providerProperties, unpooled);
- jpaProperties.put("javax.persistence.jtaDataSource", toUse);
+ jpaProperties.put(JTA_DATA_SOURCE, toUse);
+ jpaProperties.put(NON_JTA_DATA_SOURCE, toUse);
return jpaProperties;
}
@Override
protected void cleanupOnClose(Map<String, Object> jpaProperties) {
- Object o = jpaProperties.get(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE);
+ Object o = jpaProperties.get(NON_JTA_DATA_SOURCE);
if (o instanceof HikariDataSource) {
((HikariDataSource)o).close();
}
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 a8bbb6b..4b1a1b5 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
@@ -18,36 +18,19 @@
*/
package org.apache.aries.tx.control.jpa.xa.impl;
-import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_PROVIDER;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.persistence.spi.PersistenceProvider;
import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
import org.apache.aries.tx.control.jpa.common.impl.AbstractManagedJPAEMFLocator;
-import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
-import org.osgi.framework.wiring.BundleWire;
-import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.jpa.EntityManagerFactoryBuilder;
-import org.osgi.service.transaction.control.TransactionControl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class XAJPAEMFLocator extends AbstractManagedJPAEMFLocator {
- private static final Logger LOGGER = LoggerFactory.getLogger(XAJPAEMFLocator.class);
-
public XAJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
super(context, pid, jpaProperties, providerProperties, onClose);
@@ -57,183 +40,13 @@
protected AbstractJPAEntityManagerProvider getResourceProvider(BundleContext context,
EntityManagerFactoryBuilder service, ServiceReference<EntityManagerFactoryBuilder> reference,
Map<String, Object> jpaProperties, Map<String, Object> providerProperties, Runnable onClose) {
+
+ Map<String, Object> jpaProps = new HashMap<String, Object>(jpaProperties);
+ Map<String, Object> providerProps = new HashMap<String, Object>(providerProperties);
+
return new DelayedJPAEntityManagerProvider(t -> {
-
- Map<String, Object> jpaProps = new HashMap<String, Object>(jpaProperties);
- Map<String, Object> providerProps = new HashMap<String, Object>(providerProperties);
-
- setupTransactionManager(context, jpaProps, providerProps, t, reference);
-
- return new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
+ return new JPAEntityManagerProviderFactoryImpl(context).getProviderFor(service,
jpaProps, providerProps, t, onClose);
});
}
-
- private void setupTransactionManager(BundleContext context, Map<String, Object> props,
- Map<String, Object> providerProps, ThreadLocal<TransactionControl> t, ServiceReference<EntityManagerFactoryBuilder> reference) {
- String provider = (String) reference.getProperty(JPA_UNIT_PROVIDER);
-
- ServiceReference<PersistenceProvider> providerRef = getPersistenceProvider(provider, context);
-
- if(providerRef == null) {
- LOGGER.warn("Unable to find a Persistence Provider for the provider named {}, so no XA plugin can be registered. XA transactions are unlikely to function properly.", provider);
- return;
- }
-
- Bundle providerBundle = providerRef.getBundle();
- Bundle txControlProviderBundle = context.getBundle();
-
- try {
- if("org.hibernate.jpa.HibernatePersistenceProvider".equals(provider)) {
-
- String pluginClass;
-
- Bundle toUse = findSource(providerBundle, "org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder");
-
- if(toUse != null) {
-
- try {
- toUse.loadClass("org.hibernate.resource.transaction.spi.DdlTransactionIsolator");
- LOGGER.debug("Detected Hibernate 5.2.2 or above when attempting to install the XA plugin.");
- pluginClass = "org.apache.aries.tx.control.jpa.xa.plugin.hibernate.impl.Hibernate522TxControlPlatform";
- } catch (Exception e) {
- LOGGER.debug("Detected Hibernate 5.2.0 or 5.2.1 when attempting to install the XA plugin.");
- pluginClass = "org.apache.aries.tx.control.jpa.xa.plugin.hibernate.impl.Hibernate520TxControlPlatform";
- }
- } else {
- toUse = findSource(providerBundle, "org.hibernate.resource.transaction.TransactionCoordinatorBuilder");
- if(toUse != null) {
- LOGGER.debug("Detected Hibernate 5.0.x or 5.1.x or above when attempting to install the XA plugin.");
- pluginClass = "org.apache.aries.tx.control.jpa.xa.plugin.hibernate.impl.HibernateTxControlPlatform";
- } else {
- LOGGER.warn("Detected a Hibernate provider, but we were unable to load an appropriate XA plugin");
- return;
- }
- }
-
- ClassLoader pluginLoader = getPluginLoader(toUse, txControlProviderBundle);
-
- Class<?> pluginClazz = pluginLoader.loadClass(pluginClass);
- Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
- .newInstance(t);
-
- props.put("hibernate.transaction.coordinator_class", plugin);
-
- } else if("org.apache.openjpa.persistence.PersistenceProviderImpl".equals(provider)) {
-
- ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
-
- Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.plugin.openjpa.impl.OpenJPATxControlPlatform");
- Object plugin = pluginClazz.getConstructor(ThreadLocal.class)
- .newInstance(t);
-
- props.put("openjpa.ManagedRuntime", plugin);
-
- } else if("org.eclipse.persistence.jpa.PersistenceProvider".equals(provider)) {
-
- ClassLoader pluginLoader = getPluginLoader(providerBundle, txControlProviderBundle);
-
- Class<?> pluginClazz = pluginLoader.loadClass("org.apache.aries.tx.control.jpa.xa.plugin.eclipse.impl.EclipseTxControlPlatform");
-
- pluginClazz.getMethod("setTransactionControl", ThreadLocal.class)
- .invoke(null, t);
-
- props.put("eclipselink.target-server", pluginClazz.getName());
- props.put("org.apache.aries.jpa.eclipselink.plugin.types", pluginClazz);
- // This is needed to ensure that sequences can be generated in nested
- // transactions without blowing up.
- if(!props.containsKey("eclipselink.jdbc.sequence-connection-pool")) {
- props.put("eclipselink.jdbc.sequence-connection-pool", "true");
- }
-
- } else {
- LOGGER.warn("The persistence provider {} is not recognised, so no adapter plugin can be registered with it. XA transactions are unlikely to work properly", provider);
- return;
- }
- } catch (Exception e) {
- //TODO log a warning and give up
- e.printStackTrace();
- }
- }
-
- private Bundle findSource(Bundle providerBundle, String toFind) {
- try{
- providerBundle.loadClass(toFind);
- return providerBundle;
- } catch (Exception e) {
- BundleWiring wiring = providerBundle.adapt(BundleWiring.class);
- return wiring.getRequiredWires("osgi.wiring.package").stream()
- .filter(bw -> "org.hibernate".equals(bw.getCapability().getAttributes().get("osgi.wiring.package")))
- .map(BundleWire::getProviderWiring)
- .map(BundleWiring::getBundle)
- .findFirst()
- .filter(b -> {
- try {
- b.loadClass(toFind);
- return true;
- } catch (Exception e2) {
- return false;
- }
- }).orElse(null);
- }
- }
-
- private ClassLoader getPluginLoader(Bundle providerBundle, Bundle txControlProviderBundle) {
- return new ClassLoader() {
-
- ConcurrentMap<String, Class<?>> loaded = new ConcurrentHashMap<>();
-
- @Override
- public Class<?> loadClass(String name) throws ClassNotFoundException {
- if(name.startsWith("org.apache.aries.tx.control.jpa.xa.plugin")) {
-
- Class<?> c = loaded.get(name);
-
- if(c != null) {
- return c;
- }
-
- String resource = name.replace('.', '/') + ".class";
-
- try (InputStream is = txControlProviderBundle.getResource(resource).openStream()) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
- byte[] b = new byte[4096];
- int read;
- while ((read = is.read(b)) != -1) {
- baos.write(b, 0, read);
- }
- byte[] clazzBytes = baos.toByteArray();
- c = defineClass(name, clazzBytes, 0, clazzBytes.length,
- XAJPAEMFLocator.class.getProtectionDomain());
- loaded.putIfAbsent(name, c);
- return c;
- } catch (IOException e) {
- throw new ClassNotFoundException("Unable to load class " + name, e);
- }
- }
-
- if(name.startsWith("org.apache.aries.tx.control") ||
- name.startsWith("org.osgi.service.transaction.control") ||
- name.startsWith("org.slf4j")) {
- return txControlProviderBundle.loadClass(name);
- }
- return providerBundle.loadClass(name);
- }
- };
- }
-
- private ServiceReference<PersistenceProvider> getPersistenceProvider(String provider, BundleContext context) {
- if(provider == null) {
- return null;
- }
- try {
- return context.getServiceReferences(PersistenceProvider.class,
- "(javax.persistence.provider=" + provider + ")").stream()
- .findFirst()
- .orElse(null);
- } catch (InvalidSyntaxException e) {
- //TODO log a warning
- return null;
- }
- }
}
\ No newline at end of file