aries-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From timothyjw...@apache.org
Subject svn commit: r1737958 [1/2] - in /aries/trunk/tx-control: ./ tx-control-jpa-itests/ tx-control-jpa-itests/src/ tx-control-jpa-itests/src/test/ tx-control-jpa-itests/src/test/java/ tx-control-jpa-itests/src/test/java/org/ tx-control-jpa-itests/src/test/j...
Date Wed, 06 Apr 2016 10:06:08 GMT
Author: timothyjward
Date: Wed Apr  6 10:06:08 2016
New Revision: 1737958

URL: http://svn.apache.org/viewvc?rev=1737958&view=rev
Log:
[tx-control] Add support for resource local JPA using EclipseLink

Added:
    aries/trunk/tx-control/tx-control-jpa-itests/
    aries/trunk/tx-control/tx-control-jpa-itests/.gitignore
    aries/trunk/tx-control/tx-control-jpa-itests/pom.xml
    aries/trunk/tx-control/tx-control-jpa-itests/src/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/SimpleTransactionTest.java
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/entity/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/entity/Message.java
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/resources/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/resources/META-INF/
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/resources/META-INF/persistence.xml
    aries/trunk/tx-control/tx-control-provider-jpa-local/
    aries/trunk/tx-control/tx-control-provider-jpa-local/.gitignore
    aries/trunk/tx-control/tx-control-provider-jpa-local/pom.xml
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DriverDataSource.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/EntityManagerWrapper.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedEntityManagerWrapper.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/TxEntityManagerWrapper.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LifecycleAware.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManager.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/test/java/org/apache/aries/tx/control/jpa/local/impl/TxContextBindingEntityManagerTest.java
Modified:
    aries/trunk/tx-control/pom.xml

Modified: aries/trunk/tx-control/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/pom.xml?rev=1737958&r1=1737957&r2=1737958&view=diff
==============================================================================
--- aries/trunk/tx-control/pom.xml (original)
+++ aries/trunk/tx-control/pom.xml Wed Apr  6 10:06:08 2016
@@ -48,9 +48,14 @@
 				<module>tx-control-provider-jdbc-common</module>
 				<module>tx-control-provider-jdbc-local</module>
 				<module>tx-control-provider-jdbc-xa</module>
+				<module>tx-control-provider-jpa-local</module>
 				<module>tx-control-itests</module>
+				<module>tx-control-jpa-itests</module>
 			</modules>
 		</profile>
 	</profiles>
 
+	<modules>
+		<module>tx-control-provider-jpa-local</module>
+	</modules>
 </project>
\ No newline at end of file

Added: aries/trunk/tx-control/tx-control-jpa-itests/.gitignore
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-jpa-itests/.gitignore?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-jpa-itests/.gitignore (added)
+++ aries/trunk/tx-control/tx-control-jpa-itests/.gitignore Wed Apr  6 10:06:08 2016
@@ -0,0 +1 @@
+/target/

Added: aries/trunk/tx-control/tx-control-jpa-itests/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-jpa-itests/pom.xml?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-jpa-itests/pom.xml (added)
+++ aries/trunk/tx-control/tx-control-jpa-itests/pom.xml Wed Apr  6 10:06:08 2016
@@ -0,0 +1,231 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.apache.aries</groupId>
+		<artifactId>parent</artifactId>
+		<version>2.0.1</version>
+		<relativePath>../../parent/pom.xml</relativePath>
+	</parent>
+	<groupId>org.apache.aries.tx-control</groupId>
+	<artifactId>org.apache.aries.tx-control-jpa-itests</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>Apache Aries Transaction Control JPA iTests</name>
+	<description>
+        JPA Integration tests using the Transaction Control service
+    </description>
+
+	<scm>
+		<connection>
+            scm:svn:http://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-jpa-itests
+        </connection>
+		<developerConnection>
+            scm:svn:https://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-jpa-itests
+        </developerConnection>
+		<url>
+            http://svn.apache.org/viewvc/aries/trunk/proxy/tx-control/tx-control-jpa-itests
+        </url>
+	</scm>
+
+
+	<properties>
+		<exam.version>3.4.0</exam.version>
+		<url.version>1.6.0</url.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.framework</artifactId>
+			<scope>test</scope>
+			<version>5.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.tx-control</groupId>
+			<artifactId>tx-control-api</artifactId>
+			<scope>provided</scope>
+			<version>0.0.1-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.tx-control</groupId>
+			<artifactId>tx-control-service-local</artifactId>
+			<scope>test</scope>
+			<version>0.0.1-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.coordinator</artifactId>
+			<scope>test</scope>
+			<version>1.0.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.configadmin</artifactId>
+			<scope>test</scope>
+			<version>1.8.8</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.tx-control</groupId>
+			<artifactId>tx-control-provider-jpa-local</artifactId>
+			<scope>test</scope>
+			<version>0.0.1-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+            <groupId>org.eclipse.persistence</groupId>
+            <artifactId>javax.persistence</artifactId>
+			<scope>test</scope>
+            <version>2.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.jdbc</artifactId>
+            <version>1.0.0</version>
+            <scope>test</scope>
+        </dependency>
+		<dependency>
+			<groupId>com.h2database</groupId>
+			<artifactId>h2</artifactId>
+			<version>1.4.191</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.testsupport</groupId>
+			<artifactId>org.apache.aries.testsupport.unit</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- pax exam -->
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.7.7</version>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.exam</groupId>
+			<artifactId>pax-exam</artifactId>
+			<version>${exam.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.exam</groupId>
+			<artifactId>pax-exam-container-forked</artifactId>
+			<version>${exam.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.exam</groupId>
+			<artifactId>pax-exam-junit4</artifactId>
+			<version>${exam.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.exam</groupId>
+			<artifactId>pax-exam-link-mvn</artifactId>
+			<version>${exam.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.url</groupId>
+			<artifactId>pax-url-aether</artifactId>
+			<version>${url.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-core</artifactId>
+			<version>0.9.29</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-classic</artifactId>
+			<version>0.9.29</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.tinybundles</groupId>
+			<artifactId>tinybundles</artifactId>
+			<version>2.0.0</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>javax.inject</groupId>
+			<artifactId>javax.inject</artifactId>
+			<version>1</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.logging</groupId>
+			<artifactId>pax-logging-api</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.logging</groupId>
+			<artifactId>pax-logging-service</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.1</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<forkMode>pertest</forkMode>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.servicemix.tooling</groupId>
+				<artifactId>depends-maven-plugin</artifactId>
+				<version>1.2</version>
+				<executions>
+					<execution>
+						<id>generate-depends-file</id>
+						<goals>
+							<goal>generate-depends-file</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+	<profiles>
+		<profile>
+			<id>ci-build-profile</id>
+			<activation>
+				<property>
+					<name>maven.repo.local</name>
+				</property>
+			</activation>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-surefire-plugin</artifactId>
+						<configuration>
+							<!-- when the local repo location has been specified, we need to pass 
+								on this information to PAX mvn url -->
+							<argLine>-Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local}</argLine>
+						</configuration>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+
+</project>
\ No newline at end of file

Added: aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java (added)
+++ aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/AbstractJPATransactionTest.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,216 @@
+/*
+ * 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.itests;
+
+import static org.ops4j.pax.exam.CoreOptions.bootClasspathLibrary;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackage;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.CoreOptions.when;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.persistence.EntityManager;
+
+import org.apache.aries.itest.AbstractIntegrationTest;
+import org.apache.aries.tx.control.itests.entity.Message;
+import org.h2.tools.Server;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.ProbeBuilder;
+import org.ops4j.pax.exam.TestProbeBuilder;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public abstract class AbstractJPATransactionTest extends AbstractIntegrationTest {
+
+	protected TransactionControl txControl;
+
+	protected EntityManager em;
+
+	private Server server;
+
+	@Before
+	public void setUp() throws Exception {
+		
+		txControl = context().getService(TransactionControl.class, 5000);
+		
+		server = Server.createTcpServer("-tcpPort", "0");
+		server.start();
+		
+		String jdbcUrl = "jdbc:h2:tcp://127.0.0.1:" + server.getPort() + "/" + getRemoteDBPath();
+		
+		em = configuredEntityManager(jdbcUrl);
+	}
+
+	private String getRemoteDBPath() {
+		String fullResourceName = getClass().getName().replace('.', '/') + ".class";
+		
+		String resourcePath = getClass().getResource(getClass().getSimpleName() + ".class").getPath();
+		
+		File testClassesDir = new File(resourcePath.substring(0, resourcePath.length() - fullResourceName.length()));
+		
+		String dbPath = new File(testClassesDir.getParentFile(), "testdb/db1").getAbsolutePath();
+		return dbPath;
+	}
+
+	private EntityManager configuredEntityManager(String jdbcUrl) throws IOException {
+		
+		Dictionary<String, Object> props = new Hashtable<>();
+		
+		props.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, "org.h2.Driver");
+		props.put(DataSourceFactory.JDBC_URL, jdbcUrl);
+		props.put(EntityManagerFactoryBuilder.JPA_UNIT_NAME, "test-unit");
+		
+		ConfigurationAdmin cm = context().getService(ConfigurationAdmin.class, 5000);
+		
+		String pid = "org.apache.aries.tx.control.jpa.local"; 
+		
+		System.out.println("Configuring connection provider with pid " + pid);
+		
+		org.osgi.service.cm.Configuration config = cm.createFactoryConfiguration(
+				pid, null);
+		config.update(props);
+		
+		return context().getService(JPAEntityManagerProvider.class, 5000).getResource(txControl);
+	}
+	
+	@After
+	public void tearDown() {
+
+		try {
+			txControl.required(() -> 
+				em.createQuery(
+						em.getCriteriaBuilder().createCriteriaDelete(Message.class)
+				).executeUpdate());
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+		
+		clearConfiguration();
+		
+		if(server != null) {
+			server.stop();
+		}
+
+		em = null;
+	}
+
+	private void clearConfiguration() {
+		ConfigurationAdmin cm = context().getService(ConfigurationAdmin.class, 5000);
+		org.osgi.service.cm.Configuration[] cfgs = null;
+		try {
+			cfgs = cm.listConfigurations(null);
+		} catch (Exception e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+		}
+		
+		if(cfgs != null) {
+			for(org.osgi.service.cm.Configuration cfg : cfgs) {
+				try {
+					cfg.delete();
+				} catch (Exception e) {}
+			}
+			try {
+				Thread.sleep(250);
+			} catch (InterruptedException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+	}
+
+	@ProbeBuilder
+	public TestProbeBuilder probeConfiguration(TestProbeBuilder probe) {
+	    // makes sure the generated Test-Bundle contains this import!
+	    probe.setHeader("Meta-Persistence", "META-INF/persistence.xml");
+	    return probe;
+	}
+	
+	@Configuration
+	public Option[] localTxConfiguration() {
+		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)),
+				mavenBundle("org.apache.aries.testsupport", "org.apache.aries.testsupport.unit").versionAsInProject(),
+				localTxControlService(),
+				localJpaResourceProviderWithH2(),
+				eclipseLink2_3_0(),
+				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")
+				);
+	}
+
+	public Option localTxControlService() {
+		return CoreOptions.composite(
+				mavenBundle("org.apache.felix", "org.apache.felix.coordinator").versionAsInProject(),
+				mavenBundle("org.apache.aries.tx-control", "tx-control-service-local").versionAsInProject());
+	}
+
+	public Option localJpaResourceProviderWithH2() {
+		return CoreOptions.composite(
+				mavenBundle("com.h2database", "h2").versionAsInProject(),
+				mavenBundle("org.apache.aries.tx-control", "tx-control-provider-jpa-local").versionAsInProject());
+	}
+	
+	public Option eclipseLink2_3_0() {
+		return CoreOptions.composite(
+				systemPackage("javax.transaction;version=1.1"),
+				systemPackage("javax.transaction.xa;version=1.1"),
+				bootClasspathLibrary(mavenBundle("org.apache.geronimo.specs", "geronimo-jta_1.1_spec", "1.1.1")),
+				mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.jpa", "2.6.0"),
+				mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.core", "2.6.0"),
+				mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.asm", "2.6.0"),
+				mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.antlr", "2.6.0"),
+				mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.jpa.jpql", "2.6.0"),
+				mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.eclipselink.adapter", "2.3.0"));
+	}
+
+	public Option ariesJPA() {
+		return mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.container", "2.3.0");
+	}
+}

Added: aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/SimpleTransactionTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/SimpleTransactionTest.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/SimpleTransactionTest.java (added)
+++ aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/SimpleTransactionTest.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,240 @@
+/*
+ * 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.itests;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+
+import org.apache.aries.tx.control.itests.entity.Message;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class SimpleTransactionTest extends AbstractJPATransactionTest {
+
+	@Test
+	public void testTx() {
+		
+		Message message = new Message();
+		message.message = "Hello World!";
+
+		txControl.required(() -> {
+				em.persist(message);
+				return null;
+			});
+
+		assertEquals("Hello World!", txControl.notSupported(() -> {
+			return em.find(Message.class, message.id).message;
+		}));
+	}
+
+	@Test
+	public void testRollback() {
+		
+		Message message = new Message();
+		message.message = "Hello World!";
+		
+		txControl.required(() -> {
+				em.persist(message);
+				txControl.setRollbackOnly();
+				return null;
+			});
+		
+		assertEquals(Long.valueOf(0), txControl.notSupported(() -> {
+			CriteriaBuilder cb = em.getCriteriaBuilder();
+			CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
+			countQuery.select(cb.count(countQuery.from(Message.class)));
+			
+			return em.createQuery(countQuery).getSingleResult();
+		}));
+	}
+
+	@Test
+	public void testNestedTx() {
+		Message message = new Message();
+		message.message = "Hello World!";
+		
+		Message message2 = new Message();
+		message2.message = "Hello Nested World!";
+		
+		txControl.required(() -> {
+			em.persist(message);
+			
+			txControl.requiresNew(() -> {
+				em.persist(message2);
+				return null;
+			});
+			
+			return null;
+		});
+		
+		List<Message> results = txControl.notSupported(() -> {
+				CriteriaBuilder cb = em.getCriteriaBuilder();
+				CriteriaQuery<Message> query = cb.createQuery(Message.class);
+				query.orderBy(cb.asc(query.from(Message.class).get("message")));
+				
+				return em.createQuery(query).getResultList();
+			});
+		
+		System.out.println(results);
+		
+		assertEquals(2, results.size());
+		assertEquals("Hello Nested World!", results.get(0).message);
+		assertEquals("Hello World!", results.get(1).message);
+	}
+	
+	@Test
+	public void testNestedTxOuterRollback() {
+		Message message = new Message();
+		message.message = "Hello World!";
+		
+		Message message2 = new Message();
+		message2.message = "Hello Nested World!";
+		
+		txControl.required(() -> {
+			// This will not end up in the database
+			em.persist(message);
+			
+			// This should only apply to the current transaction level
+			txControl.setRollbackOnly();
+
+			// This nested transaction will commit
+			txControl.requiresNew(() -> {
+				em.persist(message2);
+				return null;
+			});
+			
+			return null;
+		});
+		
+		List<Message> results = txControl.notSupported(() -> {
+				CriteriaBuilder cb = em.getCriteriaBuilder();
+				CriteriaQuery<Message> query = cb.createQuery(Message.class);
+				query.orderBy(cb.asc(query.from(Message.class).get("message")));
+				
+				return em.createQuery(query).getResultList();
+			});
+		
+		System.out.println(results);
+		
+		assertEquals(1, results.size());
+		assertEquals("Hello Nested World!", results.get(0).message);
+	}
+
+	@Test
+	public void testNestedTxInnerRollback() {
+		
+		Message message = new Message();
+		message.message = "Hello World!";
+		
+		Message message2 = new Message();
+		message2.message = "Hello Nested World!";
+		
+		txControl.required(() -> {
+			// This will end up in the database
+			em.persist(message);
+
+			// This nested transaction will not commit
+			txControl.requiresNew(() -> {
+				em.persist(message2);
+				txControl.setRollbackOnly();
+				return null;
+			});
+			
+			return null;
+		});
+		
+		List<Message> results = txControl.notSupported(() -> {
+				CriteriaBuilder cb = em.getCriteriaBuilder();
+				CriteriaQuery<Message> query = cb.createQuery(Message.class);
+				query.orderBy(cb.asc(query.from(Message.class).get("message")));
+				
+				return em.createQuery(query).getResultList();
+			});
+		
+		System.out.println(results);
+		
+		assertEquals(1, results.size());
+		assertEquals("Hello World!", results.get(0).message);
+	}
+	
+	@Test
+	public void testRequiredInheritsTx() {
+		
+		Message message = new Message();
+		message.message = "Hello World!";
+		
+		Message message2 = new Message();
+		message2.message = "Hello Nested World!";
+		
+		txControl.required(() -> {
+			em.persist(message);
+
+			txControl.required(() -> {
+				em.persist(message2);
+				return null;
+			});
+			
+			return null;
+		});
+		
+		List<Message> results = txControl.notSupported(() -> {
+				CriteriaBuilder cb = em.getCriteriaBuilder();
+				CriteriaQuery<Message> query = cb.createQuery(Message.class);
+				query.orderBy(cb.asc(query.from(Message.class).get("message")));
+				
+				return em.createQuery(query).getResultList();
+			});
+		
+		System.out.println(results);
+		
+		assertEquals(2, results.size());
+		assertEquals("Hello Nested World!", results.get(0).message);
+		assertEquals("Hello World!", results.get(1).message);
+	}
+
+	@Test
+	public void testSuspendedTx() {
+		
+		Message message = new Message();
+		message.message = "Hello World!";
+
+		txControl.required(() -> {
+				em.persist(message);
+				
+				assertEquals(Long.valueOf(0), txControl.notSupported(() -> {
+					CriteriaBuilder cb = em.getCriteriaBuilder();
+					CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
+					countQuery.select(cb.count(countQuery.from(Message.class)));
+					
+					return em.createQuery(countQuery).getSingleResult();
+				}));                
+				
+				return null;
+			});
+	}
+}

Added: aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/entity/Message.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/entity/Message.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/entity/Message.java (added)
+++ aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/entity/Message.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,21 @@
+package org.apache.aries.tx.control.itests.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity
+public class Message {
+
+	@Id
+	@GeneratedValue
+	public Integer id;
+	
+	public String message;
+
+	@Override
+	public String toString() {
+		return "Message [id=" + id + ", message=" + message + "]";
+	}
+	
+}

Added: aries/trunk/tx-control/tx-control-jpa-itests/src/test/resources/META-INF/persistence.xml
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-jpa-itests/src/test/resources/META-INF/persistence.xml?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-jpa-itests/src/test/resources/META-INF/persistence.xml (added)
+++ aries/trunk/tx-control/tx-control-jpa-itests/src/test/resources/META-INF/persistence.xml Wed Apr  6 10:06:08 2016
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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 WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
+   version="2.0">
+  
+  <persistence-unit name="test-unit">
+    <description>Test persistence unit for the Transaction Control JPA Provider</description>
+    
+    <properties>
+        <!-- These properties are creating the database on the fly. 
+             We are using them to avoid the tests having
+             to create a database  -->
+        <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
+    </properties>
+  </persistence-unit>
+</persistence>
\ No newline at end of file

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/.gitignore
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/.gitignore?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/.gitignore (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/.gitignore Wed Apr  6 10:06:08 2016
@@ -0,0 +1 @@
+/target/

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/pom.xml?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/pom.xml (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/pom.xml Wed Apr  6 10:06:08 2016
@@ -0,0 +1,181 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.apache.aries</groupId>
+		<artifactId>parent</artifactId>
+		<version>2.0.1</version>
+		<relativePath>../../parent/pom.xml</relativePath>
+	</parent>
+	<groupId>org.apache.aries.tx-control</groupId>
+	<artifactId>tx-control-provider-jpa-local</artifactId>
+	<packaging>bundle</packaging>
+	<name>OSGi Transaction Control JPA Resource Provider - Local Transactions</name>
+	<version>0.0.1-SNAPSHOT</version>
+
+	<description>
+        This bundle contains a JPA resource provider for use with the OSGi Transaction Control Service that supports local transactions.
+    </description>
+
+	<scm>
+		<connection>
+            scm:svn:http://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-provider-jpa-local
+        </connection>
+		<developerConnection>
+            scm:svn:https://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-provider-jpa-local
+        </developerConnection>
+		<url>
+            http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local
+        </url>
+	</scm>
+
+	<properties>
+		<aries.osgi.activator>
+			org.apache.aries.tx.control.jpa.local.impl.Activator
+		</aries.osgi.activator>
+		<!-- We keep the versioning from Geronimo as it makes most things work, 
+		     even though everything should use the JavaJPA contract -->
+		<aries.osgi.export.pkg>
+			org.osgi.service.transaction.control.jpa,
+			org.osgi.service.cm,
+			org.osgi.service.jdbc,
+			org.osgi.service.jpa,
+			javax.persistence;version=1.2;jpa=2.1, 
+			javax.persistence.criteria;version=1.2;jpa=2.1, 
+			javax.persistence.metamodel;version=1.2;jpa=2.1, 
+			javax.persistence.spi;version=1.2;jpa=2.1, 
+			javax.persistence;version=2.1, 
+			javax.persistence.criteria;version=2.1, 
+			javax.persistence.metamodel;version=2.1, 
+			javax.persistence.spi;version=2.1
+		</aries.osgi.export.pkg>
+		<aries.osgi.private.pkg>
+			org.apache.aries.tx.control.jpa.*,
+			org.apache.geronimo.osgi.locator,
+			org.apache.geronimo.specs.jpa,
+			com.zaxxer.hikari,
+			com.zaxxer.hikari.metrics,
+			com.zaxxer.hikari.pool,
+			com.zaxxer.hikari.util
+		</aries.osgi.private.pkg>
+		<aries.osgi.import.pkg>
+			!com.codahale.*,
+			!com.zaxxer.hikari.metrics.dropwizard,
+			!javassist.*,
+			!org.apache.geronimo.osgi.registry.api,
+			javax.persistence;version="0.0.0",
+			javax.persistence.criteria;version="0.0.0",
+			javax.persistence.metamodel;version="0.0.0",
+			javax.persistence.spi;version="0.0.0",
+			org.osgi.service.transaction.control;version="[0.0.1,0.0.2)",
+			org.osgi.service.transaction.control.jpa;version="[0.0.1,0.0.2)",
+			org.osgi.service.cm,
+			org.osgi.service.jdbc,
+			org.osgi.service.jpa,
+			*
+		</aries.osgi.import.pkg>
+		<lastReleaseVersion>0.0.1-SNAPSHOT</lastReleaseVersion>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.tx-control</groupId>
+			<artifactId>tx-control-api</artifactId>
+			<version>0.0.1-SNAPSHOT</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.jdbc</artifactId>
+			<version>1.0.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-jpa_2.1_spec</artifactId>
+			<version>1.0-alpha-1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.jpa</artifactId>
+			<version>1.0.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.cm</artifactId>
+			<version>1.5.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.util.tracker</artifactId>
+			<version>1.5.1</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.core</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.zaxxer</groupId>
+			<artifactId>HikariCP</artifactId>
+			<version>2.4.3</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-all</artifactId>
+			<version>1.9.5</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.aries.versioning</groupId>
+				<artifactId>org.apache.aries.versioning.plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-verify</id>
+						<phase>verify</phase>
+						<goals>
+							<goal>version-check</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<!-- We have to use a newer bnd due to a bug in its handling of
+				     provided contract version lists -->
+				<version>3.0.1</version>
+				<configuration>
+					<instructions>
+						<Provide-Capability>osgi.contract;osgi.contract="JavaJPA";version:List&lt;Version&gt;="1.0,2.0,2.1";uses:="javax.persistence,javax.persistence.criteria,javax.persistence.metamodel,javax.persistence.spi"</Provide-Capability>
+						<Require-Capability>osgi.contract;filter:="(&amp;(osgi.contract=JavaJPA)(version=2.1))"</Require-Capability>
+					</instructions>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+</project>
\ No newline at end of file

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DriverDataSource.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DriverDataSource.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DriverDataSource.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/DriverDataSource.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,70 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+public class DriverDataSource implements DataSource {
+
+	private final Driver driver;
+	private final String jdbcURL;
+	private final Properties properties;
+
+	public DriverDataSource(Driver driver, String jdbcURL, Properties properties) {
+		this.driver = driver;
+		this.jdbcURL = jdbcURL;
+		this.properties = properties;
+	}
+
+	@Override
+	public PrintWriter getLogWriter() throws SQLException {
+		throw new SQLFeatureNotSupportedException("Driver based JDBC does not support log writing");
+	}
+
+	@Override
+	public void setLogWriter(PrintWriter out) throws SQLException {
+		throw new SQLFeatureNotSupportedException("Driver based JDBC does not support log writing");
+	}
+
+	@Override
+	public void setLoginTimeout(int seconds) throws SQLException {
+		throw new SQLFeatureNotSupportedException("Driver based JDBC does not support login timeouts");
+	}
+
+	@Override
+	public int getLoginTimeout() throws SQLException {
+		throw new SQLFeatureNotSupportedException("Driver based JDBC does not support login timeouts");
+	}
+
+	@Override
+	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+		throw new SQLFeatureNotSupportedException("Driver based JDBC does not support log writing");
+	}
+
+	@Override
+	public <T> T unwrap(Class<T> iface) throws SQLException {
+		throw new SQLFeatureNotSupportedException("Driver based JDBC does not support unwrapping");
+	}
+
+	@Override
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+		return false;
+	}
+
+	@Override
+	public Connection getConnection() throws SQLException {
+		return driver.connect(jdbcURL, properties);
+	}
+
+	@Override
+	public Connection getConnection(String username, String password) throws SQLException {
+		return getConnection();
+	}
+
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/EntityManagerWrapper.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/EntityManagerWrapper.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/EntityManagerWrapper.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/EntityManagerWrapper.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,229 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityGraph;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import javax.persistence.FlushModeType;
+import javax.persistence.LockModeType;
+import javax.persistence.Query;
+import javax.persistence.StoredProcedureQuery;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaDelete;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.CriteriaUpdate;
+import javax.persistence.metamodel.Metamodel;
+
+public abstract class EntityManagerWrapper implements EntityManager {
+
+	public void persist(Object entity) {
+		getRealEntityManager().persist(entity);
+	}
+
+	public <T> T merge(T entity) {
+		return getRealEntityManager().merge(entity);
+	}
+
+	public void remove(Object entity) {
+		getRealEntityManager().remove(entity);
+	}
+
+	public <T> T find(Class<T> entityClass, Object primaryKey) {
+		return getRealEntityManager().find(entityClass, primaryKey);
+	}
+
+	public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
+		return getRealEntityManager().find(entityClass, primaryKey, properties);
+	}
+
+	public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
+		return getRealEntityManager().find(entityClass, primaryKey, lockMode);
+	}
+
+	public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
+		return getRealEntityManager().find(entityClass, primaryKey, lockMode, properties);
+	}
+
+	public <T> T getReference(Class<T> entityClass, Object primaryKey) {
+		return getRealEntityManager().getReference(entityClass, primaryKey);
+	}
+
+	public void flush() {
+		getRealEntityManager().flush();
+	}
+
+	public void setFlushMode(FlushModeType flushMode) {
+		getRealEntityManager().setFlushMode(flushMode);
+	}
+
+	public FlushModeType getFlushMode() {
+		return getRealEntityManager().getFlushMode();
+	}
+
+	public void lock(Object entity, LockModeType lockMode) {
+		getRealEntityManager().lock(entity, lockMode);
+	}
+
+	public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {
+		getRealEntityManager().lock(entity, lockMode, properties);
+	}
+
+	public void refresh(Object entity) {
+		getRealEntityManager().refresh(entity);
+	}
+
+	public void refresh(Object entity, Map<String, Object> properties) {
+		getRealEntityManager().refresh(entity, properties);
+	}
+
+	public void refresh(Object entity, LockModeType lockMode) {
+		getRealEntityManager().refresh(entity, lockMode);
+	}
+
+	public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
+		getRealEntityManager().refresh(entity, lockMode, properties);
+	}
+
+	public void clear() {
+		getRealEntityManager().clear();
+	}
+
+	public void detach(Object entity) {
+		getRealEntityManager().detach(entity);
+	}
+
+	public boolean contains(Object entity) {
+		return getRealEntityManager().contains(entity);
+	}
+
+	public LockModeType getLockMode(Object entity) {
+		return getRealEntityManager().getLockMode(entity);
+	}
+
+	public void setProperty(String propertyName, Object value) {
+		getRealEntityManager().setProperty(propertyName, value);
+	}
+
+	public Map<String, Object> getProperties() {
+		return getRealEntityManager().getProperties();
+	}
+
+	public Query createQuery(String qlString) {
+		return getRealEntityManager().createQuery(qlString);
+	}
+
+	public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
+		return getRealEntityManager().createQuery(criteriaQuery);
+	}
+
+	public Query createQuery(@SuppressWarnings("rawtypes") CriteriaUpdate updateQuery) {
+		return getRealEntityManager().createQuery(updateQuery);
+	}
+
+	public Query createQuery(@SuppressWarnings("rawtypes") CriteriaDelete deleteQuery) {
+		return getRealEntityManager().createQuery(deleteQuery);
+	}
+
+	public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
+		return getRealEntityManager().createQuery(qlString, resultClass);
+	}
+
+	public Query createNamedQuery(String name) {
+		return getRealEntityManager().createNamedQuery(name);
+	}
+
+	public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
+		return getRealEntityManager().createNamedQuery(name, resultClass);
+	}
+
+	public Query createNativeQuery(String sqlString) {
+		return getRealEntityManager().createNativeQuery(sqlString);
+	}
+
+	public Query createNativeQuery(String sqlString, @SuppressWarnings("rawtypes") Class resultClass) {
+		return getRealEntityManager().createNativeQuery(sqlString, resultClass);
+	}
+
+	public Query createNativeQuery(String sqlString, String resultSetMapping) {
+		return getRealEntityManager().createNativeQuery(sqlString, resultSetMapping);
+	}
+
+	public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
+		return getRealEntityManager().createNamedStoredProcedureQuery(name);
+	}
+
+	public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
+		return getRealEntityManager().createStoredProcedureQuery(procedureName);
+	}
+
+	public StoredProcedureQuery createStoredProcedureQuery(String procedureName, @SuppressWarnings("rawtypes") Class... resultClasses) {
+		return getRealEntityManager().createStoredProcedureQuery(procedureName, resultClasses);
+	}
+
+	public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
+		return getRealEntityManager().createStoredProcedureQuery(procedureName, resultSetMappings);
+	}
+
+	public void joinTransaction() {
+		getRealEntityManager().joinTransaction();
+	}
+
+	public boolean isJoinedToTransaction() {
+		return getRealEntityManager().isJoinedToTransaction();
+	}
+
+	public <T> T unwrap(Class<T> cls) {
+		return getRealEntityManager().unwrap(cls);
+	}
+
+	public Object getDelegate() {
+		return getRealEntityManager().getDelegate();
+	}
+
+	public void close() {
+		getRealEntityManager().close();
+	}
+
+	public boolean isOpen() {
+		return getRealEntityManager().isOpen();
+	}
+
+	public EntityTransaction getTransaction() {
+		return getRealEntityManager().getTransaction();
+	}
+
+	public EntityManagerFactory getEntityManagerFactory() {
+		return getRealEntityManager().getEntityManagerFactory();
+	}
+
+	public CriteriaBuilder getCriteriaBuilder() {
+		return getRealEntityManager().getCriteriaBuilder();
+	}
+
+	public Metamodel getMetamodel() {
+		return getRealEntityManager().getMetamodel();
+	}
+
+	public <T> EntityGraph<T> createEntityGraph(Class<T> rootType) {
+		return getRealEntityManager().createEntityGraph(rootType);
+	}
+
+	public EntityGraph<?> createEntityGraph(String graphName) {
+		return getRealEntityManager().createEntityGraph(graphName);
+	}
+
+	public EntityGraph<?> getEntityGraph(String graphName) {
+		return getRealEntityManager().getEntityGraph(graphName);
+	}
+
+	public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass) {
+		return getRealEntityManager().getEntityGraphs(entityClass);
+	}
+
+	protected abstract EntityManager getRealEntityManager();
+
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedEntityManagerWrapper.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedEntityManagerWrapper.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedEntityManagerWrapper.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ScopedEntityManagerWrapper.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,33 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TransactionRequiredException;
+
+public class ScopedEntityManagerWrapper extends EntityManagerWrapper {
+
+	private final EntityManager entityManager;
+	
+	public ScopedEntityManagerWrapper(EntityManager entityManager) {
+		this.entityManager = entityManager;
+	}
+
+	@Override
+	protected EntityManager getRealEntityManager() {
+		return entityManager;
+	}
+
+	@Override
+	public void close() {
+		// A no op
+	}
+
+	@Override
+	public void joinTransaction() {
+		throw new TransactionRequiredException("This EntityManager is being used in the No Transaction scope. There is no transaction to join.");
+	}
+
+	@Override
+	public boolean isJoinedToTransaction() {
+		return false;
+	}
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/TxEntityManagerWrapper.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/TxEntityManagerWrapper.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/TxEntityManagerWrapper.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/TxEntityManagerWrapper.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,40 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+
+import org.osgi.service.transaction.control.TransactionException;
+
+public class TxEntityManagerWrapper extends EntityManagerWrapper {
+
+	private final EntityManager entityManager;
+	
+	public TxEntityManagerWrapper(EntityManager entityManager) {
+		this.entityManager = entityManager;
+	}
+
+	@Override
+	protected EntityManager getRealEntityManager() {
+		return entityManager;
+	}
+
+	@Override
+	public void close() {
+		// A no-op
+	}
+
+	@Override
+	public void joinTransaction() {
+		// A no-op
+	}
+
+	@Override
+	public boolean isJoinedToTransaction() {
+		return true;
+	}
+
+	@Override
+	public EntityTransaction getTransaction() {
+		throw new TransactionException("Programmatic transaction management is not supported for a Transactional EntityManager");
+	}
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,56 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import static org.osgi.framework.Constants.SERVICE_PID;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.geronimo.specs.jpa.PersistenceActivator;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
+
+public class Activator implements BundleActivator {
+
+	private final BundleActivator geronimoActivator;
+	
+	private ServiceRegistration<JPAEntityManagerProviderFactory> reg;
+	private ServiceRegistration<ManagedServiceFactory> factoryReg;
+	
+	public Activator() {
+		geronimoActivator = new PersistenceActivator();
+	}
+	
+	@Override
+	public void start(BundleContext context) throws Exception {
+		geronimoActivator.start(context);
+		
+		reg = context.registerService(JPAEntityManagerProviderFactory.class, 
+				new JPAEntityManagerProviderFactoryImpl(), getProperties());
+		
+		factoryReg = context.registerService(ManagedServiceFactory.class, 
+				new ManagedServiceFactoryImpl(context), getMSFProperties());
+	}
+
+	@Override
+	public void stop(BundleContext context) throws Exception {
+		reg.unregister();
+		factoryReg.unregister();
+		geronimoActivator.stop(context);
+	}
+
+	private Dictionary<String, Object> getProperties() {
+		Dictionary<String, Object> props = new Hashtable<>();
+		props.put("osgi.local.enabled", Boolean.TRUE);
+		return props;
+	}
+
+	private Dictionary<String, ?> getMSFProperties() {
+		Dictionary<String, Object> props = new Hashtable<>();
+		props.put(SERVICE_PID, "org.apache.aries.tx.control.jpa.local");
+		return props;
+	}
+
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,79 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import static java.util.Optional.ofNullable;
+import static javax.persistence.spi.PersistenceUnitTransactionType.RESOURCE_LOCAL;
+
+import java.util.Map;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.spi.PersistenceUnitTransactionType;
+
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
+
+public class JPAEntityManagerProviderFactoryImpl implements JPAEntityManagerProviderFactory {
+
+	@Override
+	public JPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties,
+			Map<String, Object> resourceProviderProperties) {
+		checkEnlistment(resourceProviderProperties);
+		
+		EntityManagerFactory emf = emfb.createEntityManagerFactory(jpaProperties);
+		
+		validateEMF(emf);
+		
+		return new JPAEntityManagerProviderImpl(emf);
+	}
+
+	private void validateEMF(EntityManagerFactory emf) {
+		Object o = emf.getProperties().get("javax.persistence.transactionType");
+		
+		PersistenceUnitTransactionType tranType;
+		if(o instanceof PersistenceUnitTransactionType) {
+			tranType = (PersistenceUnitTransactionType) o;
+		} else if (o instanceof String) {
+			tranType = PersistenceUnitTransactionType.valueOf(o.toString());
+		} else {
+			//TODO log this?
+			tranType = null;
+		}
+		
+		if(RESOURCE_LOCAL != tranType) {
+			throw new IllegalArgumentException("The supplied EntityManagerFactory is not declared RESOURCE_LOCAL");
+		}
+	}
+
+	@Override
+	public JPAEntityManagerProvider getProviderFor(EntityManagerFactory emf, Map<String, Object> jpaProperties,
+			Map<String, Object> resourceProviderProperties) {
+		checkEnlistment(resourceProviderProperties);
+		validateEMF(emf);
+		
+		return new JPAEntityManagerProviderImpl(emf);
+	}
+
+	private void checkEnlistment(Map<String, Object> resourceProviderProperties) {
+		if (toBoolean(resourceProviderProperties, XA_ENLISTMENT_ENABLED, false)) {
+			throw new TransactionException("This Resource Provider does not support XA transactions");
+		} else if (!toBoolean(resourceProviderProperties, LOCAL_ENLISTMENT_ENABLED, true)) {
+			throw new TransactionException(
+					"This Resource Provider always enlists in local transactions as it does not support XA");
+		}
+	}
+	
+	private boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
+		Object o =  ofNullable(props)
+			.map(m -> m.get(key))
+			.orElse(defaultValue);
+		
+		if (o instanceof Boolean) {
+			return ((Boolean) o).booleanValue();
+		} else if(o instanceof String) {
+			return Boolean.parseBoolean((String) o);
+		} else {
+			throw new IllegalArgumentException("The property " + key + " cannot be converted to a boolean");
+		}
+	}
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,26 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import java.util.UUID;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+
+public class JPAEntityManagerProviderImpl implements JPAEntityManagerProvider {
+
+	private final UUID					uuid	= UUID.randomUUID();
+
+	private final EntityManagerFactory 	emf;
+	
+	public JPAEntityManagerProviderImpl(EntityManagerFactory emf) {
+		this.emf = emf;
+	}
+
+	@Override
+	public EntityManager getResource(TransactionControl txControl) throws TransactionException {
+		return new TxContextBindingEntityManager(txControl, emf, uuid);
+	}
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LifecycleAware.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LifecycleAware.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LifecycleAware.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/LifecycleAware.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,8 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+public interface LifecycleAware {
+
+	public void start();
+	
+	public void stop();
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,225 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import static java.util.Optional.ofNullable;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.osgi.framework.Constants.OBJECTCLASS;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_URL;
+import static org.osgi.service.jdbc.DataSourceFactory.OSGI_JDBC_DRIVER_CLASS;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_LIFETIME;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_POOLING_ENABLED;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.CONNECTION_TIMEOUT;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.IDLE_TIMEOUT;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MAX_CONNECTIONS;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.MIN_CONNECTIONS;
+import static org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory.USE_DRIVER;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.sql.DataSource;
+
+import org.apache.aries.tx.control.jpa.common.impl.DriverDataSource;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+public class ManagedJPADataSourceSetup implements LifecycleAware,
+		ServiceTrackerCustomizer<DataSourceFactory, ManagedJPAEMFLocator> {
+
+	private final BundleContext context;
+	private final String pid;
+	private final Properties jdbcProperties;
+	private final Map<String, Object> baseJPAProperties;
+	private final Map<String, Object> providerProperties;
+	
+	private final ServiceTracker<DataSourceFactory, ManagedJPAEMFLocator> dsfTracker;
+	private final AtomicReference<ServiceReference<DataSourceFactory>> activeDsf = new AtomicReference<>();
+
+	public ManagedJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties,
+			Map<String, Object> baseJPAProperties, Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
+		this.context = context;
+		this.pid = pid;
+		this.jdbcProperties = jdbcProperties;
+		this.baseJPAProperties = baseJPAProperties;
+		this.providerProperties = providerProperties;
+
+		String targetFilter = (String) providerProperties.get(ManagedServiceFactoryImpl.DSF_TARGET_FILTER);
+		if (targetFilter == null) {
+			String driver = (String) providerProperties.get(OSGI_JDBC_DRIVER_CLASS);
+			if (driver == null) {
+				ManagedServiceFactoryImpl.LOG.error("The configuration {} must specify a target filter or a JDBC driver class", pid);
+				throw new ConfigurationException(OSGI_JDBC_DRIVER_CLASS,
+						"The configuration must specify either a target filter or a JDBC driver class");
+			}
+			targetFilter = "(" + OSGI_JDBC_DRIVER_CLASS + "=" + driver + ")";
+		}
+
+		targetFilter = "(&(" + OBJECTCLASS + "=" + DataSourceFactory.class.getName() + ")" + targetFilter + ")";
+
+		this.dsfTracker = new ServiceTracker<>(context, context.createFilter(targetFilter), this);
+	}
+
+	public void start() {
+		dsfTracker.open();
+	}
+
+	public void stop() {
+		dsfTracker.close();
+	}
+
+	@Override
+	public ManagedJPAEMFLocator addingService(ServiceReference<DataSourceFactory> reference) {
+		DataSourceFactory service = context.getService(reference);
+		ManagedJPAEMFLocator toReturn;
+		try {
+			toReturn = new ManagedJPAEMFLocator(context, pid, 
+					getJPAProperties(service), providerProperties);
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			return null;
+		}
+		updateService(reference, toReturn);
+		
+		return toReturn;
+	}
+
+	private void updateService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator locator) {
+		boolean setDsf;
+		synchronized (this) {
+			setDsf = activeDsf.compareAndSet(null, reference);
+		}
+		try {
+			if (setDsf) {
+				locator.start();
+			}
+		} catch (Exception e) {
+			ManagedServiceFactoryImpl.LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
+			activeDsf.compareAndSet(reference, null);
+			throw new IllegalStateException("An error occurred when creating the connection provider", e);
+		}
+	}
+
+	private Map<String, Object> getJPAProperties(DataSourceFactory dsf) {
+		Map<String, Object> props = new HashMap<>(baseJPAProperties);
+		
+		DataSource unpooled;
+		try {
+			if (toBoolean(providerProperties, USE_DRIVER, false)) {
+				unpooled = new DriverDataSource(dsf.createDriver(null), jdbcProperties.getProperty(JDBC_URL),
+						jdbcProperties);
+			} else {
+				unpooled = dsf.createDataSource(jdbcProperties);
+			}
+		} catch (SQLException sqle) {
+			throw new TransactionException("Unable to create the JDBC resource provider", sqle);
+		}
+
+		DataSource toUse = poolIfNecessary(providerProperties, unpooled);
+		
+		props.put("javax.persistence.nonJtaDataSource", toUse);
+		
+		return props;
+	}
+	
+	@Override
+	public void modifiedService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator service) {
+	}
+
+	@Override
+	public void removedService(ServiceReference<DataSourceFactory> reference, ManagedJPAEMFLocator service) {
+		service.stop();
+
+		if (activeDsf.compareAndSet(reference, null)) {
+			Map<ServiceReference<DataSourceFactory>,ManagedJPAEMFLocator> tracked = dsfTracker.getTracked();
+			if (!tracked.isEmpty()) {
+				Entry<ServiceReference<DataSourceFactory>, ManagedJPAEMFLocator> e = tracked.entrySet().iterator().next();
+				updateService(e.getKey(), e.getValue());
+			}
+		}
+	}
+	
+	
+	private DataSource poolIfNecessary(Map<String, Object> resourceProviderProperties, DataSource unpooled) {
+		DataSource toUse;
+
+		if (toBoolean(resourceProviderProperties, CONNECTION_POOLING_ENABLED, true)) {
+			HikariConfig hcfg = new HikariConfig();
+			hcfg.setDataSource(unpooled);
+
+			// Sizes
+			hcfg.setMaximumPoolSize(toInt(resourceProviderProperties, MAX_CONNECTIONS, 10));
+			hcfg.setMinimumIdle(toInt(resourceProviderProperties, MIN_CONNECTIONS, 10));
+
+			// Timeouts
+			hcfg.setConnectionTimeout(toLong(resourceProviderProperties, CONNECTION_TIMEOUT, SECONDS.toMillis(30)));
+			hcfg.setIdleTimeout(toLong(resourceProviderProperties, IDLE_TIMEOUT, TimeUnit.MINUTES.toMillis(3)));
+			hcfg.setMaxLifetime(toLong(resourceProviderProperties, CONNECTION_LIFETIME, HOURS.toMillis(3)));
+
+			toUse = new HikariDataSource(hcfg);
+
+		} else {
+			toUse = unpooled;
+		}
+		return toUse;
+	}
+
+	private boolean toBoolean(Map<String, Object> props, String key, boolean defaultValue) {
+		Object o =  ofNullable(props)
+			.map(m -> m.get(key))
+			.orElse(defaultValue);
+		
+		if (o instanceof Boolean) {
+			return ((Boolean) o).booleanValue();
+		} else if(o instanceof String) {
+			return Boolean.parseBoolean((String) o);
+		} else {
+			throw new IllegalArgumentException("The property " + key + " cannot be converted to a boolean");
+		}
+	}
+
+	private int toInt(Map<String, Object> props, String key, int defaultValue) {
+		
+		Object o =  ofNullable(props)
+				.map(m -> m.get(key))
+				.orElse(defaultValue);
+		
+		if (o instanceof Number) {
+			return ((Number) o).intValue();
+		} else if(o instanceof String) {
+			return Integer.parseInt((String) o);
+		} else {
+			throw new IllegalArgumentException("The property " + key + " cannot be converted to an int");
+		}
+	}
+
+	private long toLong(Map<String, Object> props, String key, long defaultValue) {
+		
+		Object o =  ofNullable(props)
+				.map(m -> m.get(key))
+				.orElse(defaultValue);
+		
+		if (o instanceof Number) {
+			return ((Number) o).longValue();
+		} else if(o instanceof String) {
+			return Long.parseLong((String) o);
+		} else {
+			throw new IllegalArgumentException("The property " + key + " cannot be converted to a long");
+		}
+	}
+
+}
\ No newline at end of file

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,139 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import static org.apache.aries.tx.control.jpa.local.impl.ManagedServiceFactoryImpl.EMF_BUILDER_TARGET_FILTER;
+import static org.osgi.framework.Constants.OBJECTCLASS;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PASSWORD;
+import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_NAME;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public class ManagedJPAEMFLocator implements LifecycleAware,
+	ServiceTrackerCustomizer<EntityManagerFactoryBuilder, EntityManagerFactoryBuilder> {
+
+	private final BundleContext context;
+	private final String pid;
+	private final Map<String, Object> jpaProperties;
+	private final Map<String, Object> providerProperties;
+	private final ServiceTracker<EntityManagerFactoryBuilder, EntityManagerFactoryBuilder> emfBuilderTracker;
+
+	private final AtomicReference<EntityManagerFactoryBuilder> activeDsf = new AtomicReference<>();
+	private final AtomicReference<ServiceRegistration<JPAEntityManagerProvider>> serviceReg = new AtomicReference<>();
+
+	public ManagedJPAEMFLocator(BundleContext context, String pid, Map<String, Object> jpaProperties,
+			Map<String, Object> providerProperties) throws InvalidSyntaxException, ConfigurationException {
+		this.context = context;
+		this.pid = pid;
+		this.jpaProperties = jpaProperties;
+		this.providerProperties = providerProperties;
+
+		String unitName = (String) providerProperties.get(JPA_UNIT_NAME);
+		if (unitName == null) {
+			ManagedServiceFactoryImpl.LOG.error("The configuration {} must specify a persistence unit name", pid);
+			throw new ConfigurationException(JPA_UNIT_NAME,
+					"The configuration must specify a persistence unit name");
+		}
+		
+		String targetFilter = (String) providerProperties.get(EMF_BUILDER_TARGET_FILTER);
+		if (targetFilter == null) {
+			targetFilter = "(" + JPA_UNIT_NAME + "=" + unitName + ")";
+		}
+
+		targetFilter = "(&(" + OBJECTCLASS + "=" + EntityManagerFactoryBuilder.class.getName() + ")" + targetFilter + ")";
+
+		this.emfBuilderTracker = new ServiceTracker<>(context, context.createFilter(targetFilter), this);
+	}
+
+	public void start() {
+		emfBuilderTracker.open();
+	}
+
+	public void stop() {
+		emfBuilderTracker.close();
+	}
+
+	@Override
+	public EntityManagerFactoryBuilder addingService(ServiceReference<EntityManagerFactoryBuilder> reference) {
+		EntityManagerFactoryBuilder service = context.getService(reference);
+
+		updateService(service);
+		return service;
+	}
+
+	private void updateService(EntityManagerFactoryBuilder service) {
+		boolean setEMFB;
+		synchronized (this) {
+			setEMFB = activeDsf.compareAndSet(null, service);
+		}
+
+		if (setEMFB) {
+			try {
+				JPAEntityManagerProvider provider = new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
+						jpaProperties, providerProperties);
+				ServiceRegistration<JPAEntityManagerProvider> reg = context
+						.registerService(JPAEntityManagerProvider.class, provider, getServiceProperties());
+				if (!serviceReg.compareAndSet(null, reg)) {
+					throw new IllegalStateException("Unable to set the JDBC connection provider registration");
+				}
+			} catch (Exception e) {
+				ManagedServiceFactoryImpl.LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
+				activeDsf.compareAndSet(service, null);
+			}
+		}
+	}
+
+	private Dictionary<String, ?> getServiceProperties() {
+		Hashtable<String, Object> props = new Hashtable<>();
+		providerProperties.keySet().stream().filter(s -> !JDBC_PASSWORD.equals(s))
+				.forEach(s -> props.put(s, providerProperties.get(s)));
+		return props;
+	}
+
+	@Override
+	public void modifiedService(ServiceReference<EntityManagerFactoryBuilder> reference, EntityManagerFactoryBuilder service) {
+	}
+
+	@Override
+	public void removedService(ServiceReference<EntityManagerFactoryBuilder> reference, EntityManagerFactoryBuilder service) {
+		boolean dsfLeft;
+		ServiceRegistration<JPAEntityManagerProvider> oldReg = null;
+		synchronized (this) {
+			dsfLeft = activeDsf.compareAndSet(service, null);
+			if (dsfLeft) {
+				oldReg = serviceReg.getAndSet(null);
+			}
+		}
+
+		if (oldReg != null) {
+			try {
+				oldReg.unregister();
+			} catch (IllegalStateException ise) {
+				ManagedServiceFactoryImpl.LOG.debug("An exception occurred when unregistering a service for {}", pid);
+			}
+		}
+		try {
+			context.ungetService(reference);
+		} catch (IllegalStateException ise) {
+			ManagedServiceFactoryImpl.LOG.debug("An exception occurred when ungetting the service for {}", reference);
+		}
+
+		if (dsfLeft) {
+			EntityManagerFactoryBuilder newEMFBuilder = emfBuilderTracker.getService();
+			if (newEMFBuilder != null) {
+				updateService(newEMFBuilder);
+			}
+		}
+	}
+}
\ No newline at end of file

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java?rev=1737958&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java Wed Apr  6 10:06:08 2016
@@ -0,0 +1,227 @@
+package org.apache.aries.tx.control.jpa.local.impl;
+
+import static java.lang.Integer.MAX_VALUE;
+import static java.util.Arrays.asList;
+import static java.util.Optional.ofNullable;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static javax.persistence.spi.PersistenceUnitTransactionType.RESOURCE_LOCAL;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATABASE_NAME;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DATASOURCE_NAME;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_DESCRIPTION;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_NETWORK_PROTOCOL;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PASSWORD;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PORT_NUMBER;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_ROLE_NAME;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_SERVER_NAME;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_URL;
+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_USER;
+import static org.osgi.service.jdbc.DataSourceFactory.OSGI_JDBC_DRIVER_CLASS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ManagedServiceFactoryImpl implements ManagedServiceFactory {
+
+	static final Logger LOG = LoggerFactory.getLogger(ManagedServiceFactoryImpl.class);
+	
+	static final String DSF_TARGET_FILTER = "aries.dsf.target.filter";
+	static final String EMF_BUILDER_TARGET_FILTER = "aries.emf.builder.target.filter";
+	static final String JDBC_PROP_NAMES = "aries.jdbc.property.names";
+	static final List<String> JDBC_PROPERTIES = asList(JDBC_DATABASE_NAME, JDBC_DATASOURCE_NAME,
+			JDBC_DESCRIPTION, JDBC_NETWORK_PROTOCOL, JDBC_PASSWORD, JDBC_PORT_NUMBER, JDBC_ROLE_NAME, JDBC_SERVER_NAME,
+			JDBC_URL, JDBC_USER);
+	static final String JPA_PROP_NAMES = "aries.jpa.property.names";
+
+	private final Map<String, LifecycleAware> managedInstances = new ConcurrentHashMap<>();
+
+	private final BundleContext context;
+
+	public ManagedServiceFactoryImpl(BundleContext context) {
+		this.context = context;
+	}
+
+	@Override
+	public String getName() {
+		return "Aries JPAEntityManagerProvider (Local only) service";
+	}
+
+	@Override
+	public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
+
+		Map<String, Object> propsMap = new HashMap<>();
+
+		Enumeration<String> keys = properties.keys();
+		while (keys.hasMoreElements()) {
+			String key = keys.nextElement();
+			propsMap.put(key, properties.get(key));
+		}
+
+		Properties jdbcProps = getJdbcProps(pid, propsMap);
+		Map<String, Object> jpaProps = getJPAProps(pid, propsMap);
+
+		try {
+			LifecycleAware worker;
+			if(propsMap.containsKey(OSGI_JDBC_DRIVER_CLASS) ||
+					propsMap.containsKey(DSF_TARGET_FILTER)) {
+				worker = new ManagedJPADataSourceSetup(context, pid, jdbcProps, jpaProps, propsMap);
+			} else {
+				if(!jdbcProps.isEmpty()) {
+					LOG.warn("The configuration {} contains raw JDBC configuration, but no osgi.jdbc.driver.class or aries.dsf.target.filter properties. No DataSourceFactory will be used byt this bundle, so the JPA provider must be able to directly create the datasource, and these configuration properties will likely be ignored. {}",
+								pid, jdbcProps.stringPropertyNames());
+				}
+				worker = new ManagedJPAEMFLocator(context, pid, jpaProps, propsMap);
+			}
+			ofNullable(managedInstances.put(pid, worker)).ifPresent(LifecycleAware::stop);
+			worker.start();
+		} catch (InvalidSyntaxException e) {
+			LOG.error("The configuration {} contained an invalid target filter {}", pid, e.getFilter());
+			throw new ConfigurationException(DSF_TARGET_FILTER, "The target filter was invalid", e);
+		}
+	}
+
+	public void stop() {
+		managedInstances.values().forEach(LifecycleAware::stop);
+	}
+
+	@SuppressWarnings("unchecked")
+	private Properties getJdbcProps(String pid, Map<String, Object> properties) throws ConfigurationException {
+
+		Object object = properties.getOrDefault(JDBC_PROP_NAMES, JDBC_PROPERTIES);
+		Collection<String> propnames;
+		if (object instanceof String) {
+			propnames = Arrays.asList(((String) object).split(","));
+		} else if (object instanceof String[]) {
+			propnames = Arrays.asList((String[]) object);
+		} else if (object instanceof Collection) {
+			propnames = (Collection<String>) object;
+		} else {
+			LOG.error("The configuration {} contained an invalid list of JDBC property names", pid, object);
+			throw new ConfigurationException(JDBC_PROP_NAMES,
+					"The jdbc property names must be a String+ or comma-separated String");
+		}
+
+		Properties p = new Properties();
+
+		propnames.stream().filter(properties::containsKey)
+				.forEach(s -> p.setProperty(s, String.valueOf(properties.get(s))));
+
+		return p;
+	}
+
+	@SuppressWarnings("unchecked")
+	private Map<String, Object> getJPAProps(String pid, Map<String, Object> properties) throws ConfigurationException {
+		
+		Object object = properties.getOrDefault(JPA_PROP_NAMES, new AllCollection());
+		Collection<String> propnames;
+		if (object instanceof String) {
+			propnames = Arrays.asList(((String) object).split(","));
+		} else if (object instanceof String[]) {
+			propnames = Arrays.asList((String[]) object);
+		} else if (object instanceof Collection) {
+			propnames = (Collection<String>) object;
+		} else {
+			LOG.error("The configuration {} contained an invalid list of JPA property names", pid, object);
+			throw new ConfigurationException(JDBC_PROP_NAMES,
+					"The jpa property names must be empty, a String+, or a comma-separated String list");
+		}
+		
+		Map<String, Object> result = properties.keySet().stream()
+			.filter(propnames::contains)
+			.collect(toMap(identity(), properties::get));
+		
+		result.putIfAbsent("javax.persistence.transactionType", RESOURCE_LOCAL.name());
+		
+		return result;
+	}
+
+	@Override
+	public void deleted(String pid) {
+		ofNullable(managedInstances.remove(pid))
+			.ifPresent(LifecycleAware::stop);
+	}
+	
+	private static class AllCollection implements Collection<String> {
+
+		@Override
+		public int size() {
+			return MAX_VALUE;
+		}
+
+		@Override
+		public boolean isEmpty() {
+			return false;
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			return true;
+		}
+
+		@Override
+		public Iterator<String> iterator() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public Object[] toArray() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public <T> T[] toArray(T[] a) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public boolean add(String e) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public boolean remove(Object o) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public boolean containsAll(Collection<?> c) {
+			return true;
+		}
+
+		@Override
+		public boolean addAll(Collection<? extends String> c) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public boolean removeAll(Collection<?> c) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public boolean retainAll(Collection<?> c) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public void clear() {
+			throw new UnsupportedOperationException();
+		}
+		
+	}
+}



Mime
View raw message