Return-Path: X-Original-To: apmail-aries-commits-archive@www.apache.org Delivered-To: apmail-aries-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 3D17B190E3 for ; Wed, 6 Apr 2016 10:06:22 +0000 (UTC) Received: (qmail 73484 invoked by uid 500); 6 Apr 2016 10:06:22 -0000 Delivered-To: apmail-aries-commits-archive@aries.apache.org Received: (qmail 73422 invoked by uid 500); 6 Apr 2016 10:06:22 -0000 Mailing-List: contact commits-help@aries.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@aries.apache.org Delivered-To: mailing list commits@aries.apache.org Received: (qmail 73411 invoked by uid 99); 6 Apr 2016 10:06:22 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 06 Apr 2016 10:06:22 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 4D3A81A4928 for ; Wed, 6 Apr 2016 10:06:21 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.799 X-Spam-Level: * X-Spam-Status: No, score=1.799 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id AwMI1vBldMQb for ; Wed, 6 Apr 2016 10:06:11 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTP id 85D2F5F23E for ; Wed, 6 Apr 2016 10:06:10 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 32CB9E0098 for ; Wed, 6 Apr 2016 10:06:09 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 1F33A3A0331 for ; Wed, 6 Apr 2016 10:06:09 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit 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 -0000 To: commits@aries.apache.org From: timothyjward@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20160406100609.1F33A3A0331@svn01-us-west.apache.org> 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 @@ tx-control-provider-jdbc-common tx-control-provider-jdbc-local tx-control-provider-jdbc-xa + tx-control-provider-jpa-local tx-control-itests + tx-control-jpa-itests + + tx-control-provider-jpa-local + \ 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 @@ + + 4.0.0 + + org.apache.aries + parent + 2.0.1 + ../../parent/pom.xml + + org.apache.aries.tx-control + org.apache.aries.tx-control-jpa-itests + 0.0.1-SNAPSHOT + Apache Aries Transaction Control JPA iTests + + JPA Integration tests using the Transaction Control service + + + + + scm:svn:http://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-jpa-itests + + + scm:svn:https://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-jpa-itests + + + http://svn.apache.org/viewvc/aries/trunk/proxy/tx-control/tx-control-jpa-itests + + + + + + 3.4.0 + 1.6.0 + + + + + org.apache.felix + org.apache.felix.framework + test + 5.0.1 + + + org.apache.aries.tx-control + tx-control-api + provided + 0.0.1-SNAPSHOT + + + org.apache.aries.tx-control + tx-control-service-local + test + 0.0.1-SNAPSHOT + + + org.apache.felix + org.apache.felix.coordinator + test + 1.0.2 + + + org.apache.felix + org.apache.felix.configadmin + test + 1.8.8 + + + org.apache.aries.tx-control + tx-control-provider-jpa-local + test + 0.0.1-SNAPSHOT + + + org.eclipse.persistence + javax.persistence + test + 2.1.1 + + + org.osgi + org.osgi.service.jdbc + 1.0.0 + test + + + com.h2database + h2 + 1.4.191 + test + + + org.apache.aries.testsupport + org.apache.aries.testsupport.unit + 2.0.0-SNAPSHOT + test + + + + + org.slf4j + slf4j-api + 1.7.7 + + + org.ops4j.pax.exam + pax-exam + ${exam.version} + test + + + org.ops4j.pax.exam + pax-exam-container-forked + ${exam.version} + test + + + org.ops4j.pax.exam + pax-exam-junit4 + ${exam.version} + test + + + org.ops4j.pax.exam + pax-exam-link-mvn + ${exam.version} + test + + + org.ops4j.pax.url + pax-url-aether + ${url.version} + test + + + ch.qos.logback + logback-core + 0.9.29 + test + + + ch.qos.logback + logback-classic + 0.9.29 + test + + + org.ops4j.pax.tinybundles + tinybundles + 2.0.0 + + + junit + junit + test + + + javax.inject + javax.inject + 1 + test + + + org.ops4j.pax.logging + pax-logging-api + test + + + org.ops4j.pax.logging + pax-logging-service + test + + + + + + + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + pertest + + + + org.apache.servicemix.tooling + depends-maven-plugin + 1.2 + + + generate-depends-file + + generate-depends-file + + + + + + + + + + ci-build-profile + + + maven.repo.local + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + -Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local} + + + + + + + + \ 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 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 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 results = txControl.notSupported(() -> { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery 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 results = txControl.notSupported(() -> { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery 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 results = txControl.notSupported(() -> { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery 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 results = txControl.notSupported(() -> { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery 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 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 @@ + + + + + + Test persistence unit for the Transaction Control JPA Provider + + + + + + + \ 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 @@ + + 4.0.0 + + org.apache.aries + parent + 2.0.1 + ../../parent/pom.xml + + org.apache.aries.tx-control + tx-control-provider-jpa-local + bundle + OSGi Transaction Control JPA Resource Provider - Local Transactions + 0.0.1-SNAPSHOT + + + This bundle contains a JPA resource provider for use with the OSGi Transaction Control Service that supports local transactions. + + + + + scm:svn:http://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-provider-jpa-local + + + scm:svn:https://svn.apache.org/repos/asf/aries/trunk/tx-control/tx-control-provider-jpa-local + + + http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jpa-local + + + + + + org.apache.aries.tx.control.jpa.local.impl.Activator + + + + 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 + + + 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 + + + !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, + * + + 0.0.1-SNAPSHOT + + + + + org.slf4j + slf4j-api + + + org.apache.aries.tx-control + tx-control-api + 0.0.1-SNAPSHOT + provided + + + org.osgi + org.osgi.service.jdbc + 1.0.0 + provided + + + org.apache.geronimo.specs + geronimo-jpa_2.1_spec + 1.0-alpha-1 + + + org.osgi + org.osgi.service.jpa + 1.0.0 + provided + + + org.osgi + org.osgi.service.cm + 1.5.0 + provided + + + org.osgi + org.osgi.util.tracker + 1.5.1 + provided + + + org.osgi + org.osgi.core + provided + + + com.zaxxer + HikariCP + 2.4.3 + provided + + + junit + junit + test + + + org.mockito + mockito-all + 1.9.5 + test + + + + + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.aries.versioning + org.apache.aries.versioning.plugin + + + default-verify + verify + + version-check + + + + + + org.apache.felix + maven-bundle-plugin + + 3.0.1 + + + osgi.contract;osgi.contract="JavaJPA";version:List<Version>="1.0,2.0,2.1";uses:="javax.persistence,javax.persistence.criteria,javax.persistence.metamodel,javax.persistence.spi" + osgi.contract;filter:="(&(osgi.contract=JavaJPA)(version=2.1))" + + + + + + \ 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 unwrap(Class 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 merge(T entity) { + return getRealEntityManager().merge(entity); + } + + public void remove(Object entity) { + getRealEntityManager().remove(entity); + } + + public T find(Class entityClass, Object primaryKey) { + return getRealEntityManager().find(entityClass, primaryKey); + } + + public T find(Class entityClass, Object primaryKey, Map properties) { + return getRealEntityManager().find(entityClass, primaryKey, properties); + } + + public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { + return getRealEntityManager().find(entityClass, primaryKey, lockMode); + } + + public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { + return getRealEntityManager().find(entityClass, primaryKey, lockMode, properties); + } + + public T getReference(Class 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 properties) { + getRealEntityManager().lock(entity, lockMode, properties); + } + + public void refresh(Object entity) { + getRealEntityManager().refresh(entity); + } + + public void refresh(Object entity, Map properties) { + getRealEntityManager().refresh(entity, properties); + } + + public void refresh(Object entity, LockModeType lockMode) { + getRealEntityManager().refresh(entity, lockMode); + } + + public void refresh(Object entity, LockModeType lockMode, Map 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 getProperties() { + return getRealEntityManager().getProperties(); + } + + public Query createQuery(String qlString) { + return getRealEntityManager().createQuery(qlString); + } + + public TypedQuery createQuery(CriteriaQuery 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 TypedQuery createQuery(String qlString, Class resultClass) { + return getRealEntityManager().createQuery(qlString, resultClass); + } + + public Query createNamedQuery(String name) { + return getRealEntityManager().createNamedQuery(name); + } + + public TypedQuery createNamedQuery(String name, Class 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 unwrap(Class 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 EntityGraph createEntityGraph(Class rootType) { + return getRealEntityManager().createEntityGraph(rootType); + } + + public EntityGraph createEntityGraph(String graphName) { + return getRealEntityManager().createEntityGraph(graphName); + } + + public EntityGraph getEntityGraph(String graphName) { + return getRealEntityManager().getEntityGraph(graphName); + } + + public List> getEntityGraphs(Class 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 reg; + private ServiceRegistration 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 getProperties() { + Dictionary props = new Hashtable<>(); + props.put("osgi.local.enabled", Boolean.TRUE); + return props; + } + + private Dictionary getMSFProperties() { + Dictionary 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 jpaProperties, + Map 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 jpaProperties, + Map resourceProviderProperties) { + checkEnlistment(resourceProviderProperties); + validateEMF(emf); + + return new JPAEntityManagerProviderImpl(emf); + } + + private void checkEnlistment(Map 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 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 { + + private final BundleContext context; + private final String pid; + private final Properties jdbcProperties; + private final Map baseJPAProperties; + private final Map providerProperties; + + private final ServiceTracker dsfTracker; + private final AtomicReference> activeDsf = new AtomicReference<>(); + + public ManagedJPADataSourceSetup(BundleContext context, String pid, Properties jdbcProperties, + Map baseJPAProperties, Map 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 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 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 getJPAProperties(DataSourceFactory dsf) { + Map 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 reference, ManagedJPAEMFLocator service) { + } + + @Override + public void removedService(ServiceReference reference, ManagedJPAEMFLocator service) { + service.stop(); + + if (activeDsf.compareAndSet(reference, null)) { + Map,ManagedJPAEMFLocator> tracked = dsfTracker.getTracked(); + if (!tracked.isEmpty()) { + Entry, ManagedJPAEMFLocator> e = tracked.entrySet().iterator().next(); + updateService(e.getKey(), e.getValue()); + } + } + } + + + private DataSource poolIfNecessary(Map 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 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 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 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 { + + private final BundleContext context; + private final String pid; + private final Map jpaProperties; + private final Map providerProperties; + private final ServiceTracker emfBuilderTracker; + + private final AtomicReference activeDsf = new AtomicReference<>(); + private final AtomicReference> serviceReg = new AtomicReference<>(); + + public ManagedJPAEMFLocator(BundleContext context, String pid, Map jpaProperties, + Map 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 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 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 getServiceProperties() { + Hashtable 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 reference, EntityManagerFactoryBuilder service) { + } + + @Override + public void removedService(ServiceReference reference, EntityManagerFactoryBuilder service) { + boolean dsfLeft; + ServiceRegistration 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 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 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 properties) throws ConfigurationException { + + Map propsMap = new HashMap<>(); + + Enumeration keys = properties.keys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + propsMap.put(key, properties.get(key)); + } + + Properties jdbcProps = getJdbcProps(pid, propsMap); + Map 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 properties) throws ConfigurationException { + + Object object = properties.getOrDefault(JDBC_PROP_NAMES, JDBC_PROPERTIES); + Collection 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) 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 getJPAProps(String pid, Map properties) throws ConfigurationException { + + Object object = properties.getOrDefault(JPA_PROP_NAMES, new AllCollection()); + Collection 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) 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 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 { + + @Override + public int size() { + return MAX_VALUE; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object o) { + return true; + } + + @Override + public Iterator iterator() { + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public 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 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(); + } + + } +}