You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ti...@apache.org on 2016/09/21 16:37:58 UTC

svn commit: r1761786 - in /aries/trunk/tx-control: tx-control-api/src/main/java/org/osgi/service/transaction/control/jpa/ tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/ tx-control-provider-jpa-local/src/main/java/org/apache/ari...

Author: timothyjward
Date: Wed Sep 21 16:37:58 2016
New Revision: 1761786

URL: http://svn.apache.org/viewvc?rev=1761786&view=rev
Log:
ARIES-1616 Clean up Local JPA resources properly

Added:
    aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractJPAEntityManagerProvider.java
      - copied, changed from r1761694, 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/common/impl/InternalJPAEntityManagerProviderFactory.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAEntityManagerProviderFactoryServiceFactory.java
    aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ResourceTrackingJPAEntityManagerProviderFactory.java
Modified:
    aries/trunk/tx-control/tx-control-api/src/main/java/org/osgi/service/transaction/control/jpa/JPAEntityManagerProviderFactory.java
    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/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

Modified: aries/trunk/tx-control/tx-control-api/src/main/java/org/osgi/service/transaction/control/jpa/JPAEntityManagerProviderFactory.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-api/src/main/java/org/osgi/service/transaction/control/jpa/JPAEntityManagerProviderFactory.java?rev=1761786&r1=1761785&r2=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-api/src/main/java/org/osgi/service/transaction/control/jpa/JPAEntityManagerProviderFactory.java (original)
+++ aries/trunk/tx-control/tx-control-api/src/main/java/org/osgi/service/transaction/control/jpa/JPAEntityManagerProviderFactory.java Wed Sep 21 16:37:58 2016
@@ -24,9 +24,9 @@ import org.osgi.service.jpa.EntityManage
 import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider;
 
 /**
- * A factory for creating JDBCConnectionProvider instances
+ * A factory for creating JPAEntityManagerProvider instances
  * <p>
- * This factory can be used if the {@link JDBCConnectionProvider} should not be
+ * This factory can be used if the {@link JPAEntityManagerProvider} should not be
  * a public service, for example to protect a username/password.
  */
 public interface JPAEntityManagerProviderFactory {

Added: aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java?rev=1761786&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java (added)
+++ aries/trunk/tx-control/tx-control-jpa-itests/src/test/java/org/apache/aries/tx/control/itests/JPALifecycleTest.java Wed Sep 21 16:37:58 2016
@@ -0,0 +1,258 @@
+/*
+ * 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 java.util.stream.Collectors.toList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.bootClasspathLibrary;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.systemPackage;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.apache.aries.tx.control.itests.entity.Message;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+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.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.transaction.control.ScopedWorkException;
+import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class JPALifecycleTest extends AbstractJPATransactionTest {
+
+	private static final long LIFETIME = 30000;
+	
+	private static final int CONNECTIONS = 17;
+	
+	protected String ariesJPAVersion() {
+		return "2.4.0";
+	}
+	
+	protected Option jpaProvider() {
+		return CoreOptions.composite(
+			// Add JTA 1.1 as a system package because of the link to javax.sql
+			// Also set javax.xml.stream to 1.0 due to hibernate's funny packaging
+			
+			systemProperty(ARIES_EMF_BUILDER_TARGET_FILTER)
+				.value("(osgi.unit.provider=org.hibernate.jpa.HibernatePersistenceProvider)"),
+			systemPackage("javax.xml.stream;version=1.0"),
+			systemPackage("javax.xml.stream.events;version=1.0"),
+			systemPackage("javax.xml.stream.util;version=1.0"),
+			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")).beforeFramework(),
+			
+			// Hibernate bundles and their dependencies (JPA API is available from the tx-control)
+			mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.antlr", "2.7.7_5"),
+			mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.dom4j", "1.6.1_5"),
+			mavenBundle("org.javassist", "javassist", "3.18.1-GA"),
+			mavenBundle("org.jboss.logging", "jboss-logging", "3.3.0.Final"),
+			mavenBundle("org.jboss", "jandex", "2.0.0.Final"),
+			mavenBundle("org.hibernate.common", "hibernate-commons-annotations", "5.0.1.Final"),
+			mavenBundle("org.hibernate", "hibernate-core", "5.0.9.Final"),
+			mavenBundle("org.hibernate", "hibernate-osgi", "5.0.9.Final"),
+			mavenBundle("org.hibernate", "hibernate-entitymanager", "5.0.9.Final"));
+	}
+	
+	@Override
+	protected Dictionary<String, Object> getBaseProperties() {
+		// Set a short lifecycle for pooled connections and force a non-standard number
+		Dictionary<String, Object> config = new Hashtable<>();
+		config.put(JDBCConnectionProviderFactory.IDLE_TIMEOUT, LIFETIME/2);
+		config.put(JDBCConnectionProviderFactory.CONNECTION_LIFETIME, LIFETIME);
+		config.put(JDBCConnectionProviderFactory.MAX_CONNECTIONS, CONNECTIONS);
+		config.put(JDBCConnectionProviderFactory.MIN_CONNECTIONS, CONNECTIONS);
+		
+		return config;
+	}
+
+	@Test
+	public void testStopOfTxControlBundle() {
+		// Do not run for XA tests yet
+		Assume.assumeFalse(Boolean.getBoolean(IS_XA));
+		
+		doBundleStoppingTest(b -> b.getSymbolicName().contains("tx-control-service"),
+				"The transaction control service is closed");
+	}
+
+	@Test
+	public void testStopOfJPABundle() {
+		// Do not run for XA tests yet
+		Assume.assumeFalse(Boolean.getBoolean(IS_XA));
+		
+		doBundleStoppingTest(b -> b.getSymbolicName().contains("tx-control-provider-jpa"),
+				"There was a problem getting hold of a database connection");
+	}
+
+	private void doBundleStoppingTest(Predicate<Bundle> p, String exceptionMessage) {
+		Message m = new Message();
+		m.message = "Hello World";
+		txControl.required(() -> {em.persist(m); return null;});
+
+		assertEquals(m.message, txControl.notSupported(() -> em.find(Message.class, m.id).message));
+
+		List<Bundle> toStop = Arrays.stream(context.getBundles()).filter(p).collect(toList());
+
+		System.out.println(toStop);
+
+		try {
+			toStop.stream().forEach(b -> {
+				System.out.println("Stopping " + b.getSymbolicName());
+				try {
+					b.stop();
+				} catch (BundleException e) {
+				}
+			});
+
+			try {
+				assertEquals(m.message, txControl.notSupported(() -> em.find(Message.class, m.id).message));
+				fail("Should not be accessible");
+			} catch (ScopedWorkException swe) {
+				assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);
+				assertEquals(exceptionMessage, swe.getCause().getMessage());
+			} catch (TransactionException te) {
+				assertEquals(exceptionMessage, te.getMessage());
+			}
+		} finally {
+			toStop.stream().forEach(b -> {
+				System.out.println("Restarting " + b.getSymbolicName());
+				try {
+					b.start();
+				} catch (BundleException e) {
+				}
+			});
+			getService(JPAEntityManagerProvider.class, 5000);
+		}
+	}
+
+	@Test
+	public void testDeleteOfConfig() throws Exception {
+		
+		// Do not run for XA tests yet
+		Assume.assumeFalse(Boolean.getBoolean(IS_XA));
+
+		Message m = new Message();
+		m.message = "Hello World";
+		txControl.required(() -> {em.persist(m); return null;});
+
+		assertEquals(m.message, txControl.notSupported(() -> em.find(Message.class, m.id).message));
+
+		ConfigurationAdmin cm = getService(ConfigurationAdmin.class, 5000);
+
+		Configuration[] configurations = cm
+				.listConfigurations("(service.factoryPid=org.apache.aries.tx.control.jpa.*)");
+
+		assertNotNull(configurations);
+		assertEquals(1, configurations.length);
+
+		configurations[0].delete();
+
+		Thread.sleep(2000);
+
+		try {
+			assertEquals(m.message, txControl.notSupported(() -> em.find(Message.class, m.id).message));
+			fail("Should not be accessible");
+		} catch (ScopedWorkException swe) {
+			assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);
+			assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());
+		}
+	}
+
+	@Test
+	public void testUpdateOfConfig() throws Exception {
+		// Do not run for XA tests yet
+		Assume.assumeFalse(Boolean.getBoolean(IS_XA));
+		
+		Message m = new Message();
+		m.message = "Hello World";
+		txControl.required(() -> {em.persist(m); return null;});
+
+		assertEquals(m.message, txControl.notSupported(() -> em.find(Message.class, m.id).message));
+
+		ConfigurationAdmin cm = getService(ConfigurationAdmin.class, 5000);
+
+		Configuration[] configurations = cm
+				.listConfigurations("(service.factoryPid=org.apache.aries.tx.control.jpa.*)");
+
+		assertNotNull(configurations);
+		assertEquals(1, configurations.length);
+
+		configurations[0].update();
+
+		Thread.sleep(2000);
+
+		try {
+			assertEquals(m.message, txControl.notSupported(() -> em.find(Message.class, m.id).message));
+			fail("Should not be accessible");
+		} catch (ScopedWorkException swe) {
+			assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);
+			assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());
+		}
+	}
+//
+//	@Test
+//	public void testReleaseOfFactoryService() {
+//		Assume.assumeFalse("Not a factory test", isConfigured());
+//
+//		txControl.required(
+//				() -> connection.createStatement().execute("Insert into TEST_TABLE values ( 'Hello World!' )"));
+//
+//		assertEquals("Hello World!", txControl.notSupported(() -> {
+//			ResultSet rs = connection.createStatement().executeQuery("Select * from TEST_TABLE");
+//			rs.next();
+//			return rs.getString(1);
+//		}));
+//
+//		trackers.stream().filter(t -> t.getService() instanceof JDBCConnectionProviderFactory).findFirst().get()
+//				.close();
+//		;
+//
+//		try {
+//			assertEquals("Hello World!", txControl.notSupported(() -> {
+//				ResultSet rs = connection.createStatement().executeQuery("Select * from TEST_TABLE");
+//				rs.next();
+//				return rs.getString(1);
+//			}));
+//			fail("Should not be accessible");
+//		} catch (ScopedWorkException swe) {
+//			assertTrue(swe.getCause().toString(), swe.getCause() instanceof TransactionException);
+//			assertEquals("There was a problem getting hold of a database connection", swe.getCause().getMessage());
+//		}
+//	}
+}

Copied: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractJPAEntityManagerProvider.java (from r1761694, 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/common/impl/AbstractJPAEntityManagerProvider.java?p2=aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractJPAEntityManagerProvider.java&p1=aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java&r1=1761694&r2=1761786&rev=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java (original)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/AbstractJPAEntityManagerProvider.java Wed Sep 21 16:37:58 2016
@@ -16,9 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.aries.tx.control.jpa.local.impl;
-
-import java.util.UUID;
+package org.apache.aries.tx.control.jpa.common.impl;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
@@ -26,19 +24,34 @@ import javax.persistence.EntityManagerFa
 import org.osgi.service.transaction.control.TransactionControl;
 import org.osgi.service.transaction.control.TransactionException;
 import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class JPAEntityManagerProviderImpl implements JPAEntityManagerProvider {
+public abstract class AbstractJPAEntityManagerProvider implements JPAEntityManagerProvider {
 
-	private final UUID					uuid	= UUID.randomUUID();
+	private static final Logger LOG = LoggerFactory.getLogger(AbstractJPAEntityManagerProvider.class);
+	
+	protected final EntityManagerFactory emf;
 
-	private final EntityManagerFactory 	emf;
+	private final Runnable onClose;
 	
-	public JPAEntityManagerProviderImpl(EntityManagerFactory emf) {
+	public AbstractJPAEntityManagerProvider(EntityManagerFactory emf, Runnable onClose) {
 		this.emf = emf;
+		this.onClose = onClose;
 	}
 
 	@Override
-	public EntityManager getResource(TransactionControl txControl) throws TransactionException {
-		return new TxContextBindingEntityManager(txControl, emf, uuid);
+	public abstract EntityManager getResource(TransactionControl txControl)
+			throws TransactionException;
+
+	
+	public void close() {
+		if(onClose != null) {
+			try {
+				onClose.run();
+			} catch (Exception e) {
+				LOG.warn("An error occurred shutting down the JPAEntityManagerProvider {}", emf, e);
+			}
+		}
 	}
 }

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/InternalJPAEntityManagerProviderFactory.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/InternalJPAEntityManagerProviderFactory.java?rev=1761786&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/InternalJPAEntityManagerProviderFactory.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/InternalJPAEntityManagerProviderFactory.java Wed Sep 21 16:37:58 2016
@@ -0,0 +1,24 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import java.util.Map;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
+
+public interface InternalJPAEntityManagerProviderFactory extends JPAEntityManagerProviderFactory {
+
+	@Override
+	AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, 
+			Map<String, Object> jpaProperties, Map<String, Object> resourceProviderProperties);
+
+	AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, 
+	Map<String, Object> jpaProperties, Map<String, Object> resourceProviderProperties, 
+	Runnable onClose);
+
+	@Override
+	AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactory emf,
+			Map<String, Object> resourceProviderProperties);
+
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAEntityManagerProviderFactoryServiceFactory.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/JPAEntityManagerProviderFactoryServiceFactory.java?rev=1761786&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAEntityManagerProviderFactoryServiceFactory.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/JPAEntityManagerProviderFactoryServiceFactory.java Wed Sep 21 16:37:58 2016
@@ -0,0 +1,38 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+public abstract class JPAEntityManagerProviderFactoryServiceFactory implements 
+	ServiceFactory<ResourceTrackingJPAEntityManagerProviderFactory> {
+
+	Set<ResourceTrackingJPAEntityManagerProviderFactory> factories = new CopyOnWriteArraySet<>();
+	
+	@Override
+	public ResourceTrackingJPAEntityManagerProviderFactory getService(Bundle bundle,
+			ServiceRegistration<ResourceTrackingJPAEntityManagerProviderFactory> registration) {
+		ResourceTrackingJPAEntityManagerProviderFactory factory = new ResourceTrackingJPAEntityManagerProviderFactory(
+						getInternalJPAEntityManagerProviderFactory());
+		factories.add(factory);
+		return factory;
+	}
+
+	@Override
+	public void ungetService(Bundle bundle, ServiceRegistration<ResourceTrackingJPAEntityManagerProviderFactory> registration,
+			ResourceTrackingJPAEntityManagerProviderFactory service) {
+		factories.remove(service);
+		service.closeAll();
+	}
+	
+	public void close() {
+		factories.stream()
+			.forEach(r -> r.closeAll());
+	}
+	
+	protected abstract InternalJPAEntityManagerProviderFactory 
+		getInternalJPAEntityManagerProviderFactory();
+}

Added: aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ResourceTrackingJPAEntityManagerProviderFactory.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/ResourceTrackingJPAEntityManagerProviderFactory.java?rev=1761786&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ResourceTrackingJPAEntityManagerProviderFactory.java (added)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/common/impl/ResourceTrackingJPAEntityManagerProviderFactory.java Wed Sep 21 16:37:58 2016
@@ -0,0 +1,75 @@
+package org.apache.aries.tx.control.jpa.common.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProvider;
+import org.osgi.service.transaction.control.jpa.JPAEntityManagerProviderFactory;
+
+class ResourceTrackingJPAEntityManagerProviderFactory implements
+	JPAEntityManagerProviderFactory {
+
+	private final List<AbstractJPAEntityManagerProvider> toClose = new ArrayList<>();
+	
+	private final InternalJPAEntityManagerProviderFactory factory;
+	
+	private boolean closed;
+	
+	public ResourceTrackingJPAEntityManagerProviderFactory(InternalJPAEntityManagerProviderFactory factory) {
+		this.factory = factory;
+	}
+
+	@Override
+	public JPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties,
+			Map<String, Object> resourceProviderProperties) {
+		return doGetResult(() -> factory.getProviderFor(emfb, 
+				jpaProperties, resourceProviderProperties));
+	}
+
+	@Override
+	public JPAEntityManagerProvider getProviderFor(EntityManagerFactory emf, Map<String, Object> resourceProviderProperties) {
+		return doGetResult(() -> factory.getProviderFor(emf, 
+				resourceProviderProperties));
+	}
+
+	private AbstractJPAEntityManagerProvider doGetResult(Supplier<AbstractJPAEntityManagerProvider> getter) {
+		synchronized (getter) {
+			if (closed) {
+				throw new IllegalStateException("This ResourceProvider has been reclaimed because the factory service that provided it was released");
+			}
+		}
+		AbstractJPAEntityManagerProvider ajcp = getter.get();
+		boolean destroy = false;
+		synchronized (toClose) {
+			if (closed) {
+				destroy = true;
+			} else {
+			    toClose.add(ajcp);
+			}
+		}
+		if(destroy) {
+			ajcp.close();
+			throw new IllegalStateException("This ResourceProvider has been reclaimed because the factory service that provided it was released");
+		}
+		return ajcp;
+	}
+
+	public void closeAll() {
+		synchronized (toClose) {
+			closed = true;
+		}
+		// toClose is now up to date and no other thread will write it
+		toClose.stream().forEach(ajcp -> {
+			try {
+				ajcp.close();
+			} catch (Exception e) {}
+		});
+		
+		toClose.clear();
+	}
+}
\ No newline at end of file

Modified: 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=1761786&r1=1761785&r2=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java (original)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/Activator.java Wed Sep 21 16:37:58 2016
@@ -23,6 +23,8 @@ import static org.osgi.framework.Constan
 import java.util.Dictionary;
 import java.util.Hashtable;
 
+import org.apache.aries.tx.control.jpa.common.impl.InternalJPAEntityManagerProviderFactory;
+import org.apache.aries.tx.control.jpa.common.impl.JPAEntityManagerProviderFactoryServiceFactory;
 import org.apache.geronimo.specs.jpa.PersistenceActivator;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -34,7 +36,10 @@ public class Activator implements Bundle
 
 	private final BundleActivator geronimoActivator;
 	
-	private ServiceRegistration<JPAEntityManagerProviderFactory> reg;
+	private JPAEntityManagerProviderFactoryServiceFactory service;
+	private ManagedServiceFactoryImpl msf;
+	
+	private ServiceRegistration<?> reg;
 	private ServiceRegistration<ManagedServiceFactory> factoryReg;
 	
 	public Activator() {
@@ -45,20 +50,47 @@ public class Activator implements Bundle
 	public void start(BundleContext context) throws Exception {
 		geronimoActivator.start(context);
 		
-		reg = context.registerService(JPAEntityManagerProviderFactory.class, 
-				new JPAEntityManagerProviderFactoryImpl(), getProperties());
+		InternalJPAEntityManagerProviderFactory ijempf = new JPAEntityManagerProviderFactoryImpl();
+		
+		service = new JPAEntityManagerProviderFactoryServiceFactory() {
+			@Override
+			protected InternalJPAEntityManagerProviderFactory getInternalJPAEntityManagerProviderFactory() {
+				return ijempf;
+			}
+		};
+		reg = context.registerService(JPAEntityManagerProviderFactory.class.getName(), 
+				service, getProperties());
 		
+		msf  = new ManagedServiceFactoryImpl(context);
 		factoryReg = context.registerService(ManagedServiceFactory.class, 
-				new ManagedServiceFactoryImpl(context), getMSFProperties());
+				msf, getMSFProperties());
 	}
 
 	@Override
 	public void stop(BundleContext context) throws Exception {
-		reg.unregister();
-		factoryReg.unregister();
+		safeUnregister(reg);
+		safeUnregister(factoryReg);
+		try {
+			msf.stop();
+		} catch (Exception e) {
+			// TODO log this
+		}
+		try {
+			service.close();
+		} catch (Exception e) {
+			// TODO log this
+		}
 		geronimoActivator.stop(context);
 	}
 
+	private void safeUnregister(ServiceRegistration<?> reg) {
+		try {
+			reg.unregister();
+		} catch (IllegalStateException ise) {
+			// Ignore this
+		}
+	}
+
 	private Dictionary<String, Object> getProperties() {
 		Dictionary<String, Object> props = new Hashtable<>();
 		props.put("osgi.local.enabled", Boolean.TRUE);

Modified: 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=1761786&r1=1761785&r2=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java (original)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderFactoryImpl.java Wed Sep 21 16:37:58 2016
@@ -26,15 +26,15 @@ import java.util.Map;
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.spi.PersistenceUnitTransactionType;
 
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
+import org.apache.aries.tx.control.jpa.common.impl.InternalJPAEntityManagerProviderFactory;
 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 {
+public class JPAEntityManagerProviderFactoryImpl implements InternalJPAEntityManagerProviderFactory {
 
 	@Override
-	public JPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties,
+	public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties,
 			Map<String, Object> resourceProviderProperties) {
 		checkEnlistment(resourceProviderProperties);
 		
@@ -42,9 +42,30 @@ public class JPAEntityManagerProviderFac
 		
 		validateEMF(emf);
 		
-		return new JPAEntityManagerProviderImpl(emf);
+		return new JPAEntityManagerProviderImpl(emf, () -> emf.close());
 	}
 
+	@Override
+		public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, 
+				Map<String, Object> jpaProperties, Map<String, Object> resourceProviderProperties, 
+				Runnable onClose) {
+			checkEnlistment(resourceProviderProperties);
+			
+			EntityManagerFactory emf = emfb.createEntityManagerFactory(jpaProperties);
+			
+			validateEMF(emf);
+			
+			return new JPAEntityManagerProviderImpl(emf, () -> {
+				try {
+					emf.close();
+				} catch (Exception e) {
+				}
+				if (onClose != null) {
+					onClose.run();
+				}
+			});
+		}
+
 	private void validateEMF(EntityManagerFactory emf) {
 		Object o = emf.getProperties().get("javax.persistence.transactionType");
 		
@@ -64,12 +85,12 @@ public class JPAEntityManagerProviderFac
 	}
 
 	@Override
-	public JPAEntityManagerProvider getProviderFor(EntityManagerFactory emf,
+	public AbstractJPAEntityManagerProvider getProviderFor(EntityManagerFactory emf,
 			Map<String, Object> resourceProviderProperties) {
 		checkEnlistment(resourceProviderProperties);
 		validateEMF(emf);
 		
-		return new JPAEntityManagerProviderImpl(emf);
+		return new JPAEntityManagerProviderImpl(emf, null);
 	}
 
 	private void checkEnlistment(Map<String, Object> resourceProviderProperties) {

Modified: 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=1761786&r1=1761785&r2=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java (original)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/JPAEntityManagerProviderImpl.java Wed Sep 21 16:37:58 2016
@@ -23,18 +23,16 @@ import java.util.UUID;
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
 
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
 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 {
+public class JPAEntityManagerProviderImpl extends AbstractJPAEntityManagerProvider {
 
 	private final UUID					uuid	= UUID.randomUUID();
 
-	private final EntityManagerFactory 	emf;
-	
-	public JPAEntityManagerProviderImpl(EntityManagerFactory emf) {
-		this.emf = emf;
+	public JPAEntityManagerProviderImpl(EntityManagerFactory emf, Runnable onClose) {
+		super(emf, onClose);
 	}
 
 	@Override

Modified: 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=1761786&r1=1761785&r2=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java (original)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPADataSourceSetup.java Wed Sep 21 16:37:58 2016
@@ -58,6 +58,8 @@ import com.zaxxer.hikari.HikariDataSourc
 public class ManagedJPADataSourceSetup implements LifecycleAware,
 		ServiceTrackerCustomizer<DataSourceFactory, ManagedJPAEMFLocator> {
 
+	private static final String JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE = "javax.persistence.nonJtaDataSource";
+	
 	private final BundleContext context;
 	private final String pid;
 	private final Properties jdbcProperties;
@@ -105,7 +107,12 @@ public class ManagedJPADataSourceSetup i
 		ManagedJPAEMFLocator toReturn;
 		try {
 			toReturn = new ManagedJPAEMFLocator(context, pid, 
-					getJPAProperties(service), providerProperties);
+					getJPAProperties(service), providerProperties, () -> {
+						Object o = providerProperties.get(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE);
+						if (o instanceof HikariDataSource) {
+							((HikariDataSource)o).close();
+						}
+					});
 		} catch (Exception e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();
@@ -149,7 +156,7 @@ public class ManagedJPADataSourceSetup i
 
 		DataSource toUse = poolIfNecessary(providerProperties, unpooled);
 		
-		props.put("javax.persistence.nonJtaDataSource", toUse);
+		props.put(JAVAX_PERSISTENCE_NON_JTA_DATA_SOURCE, toUse);
 		
 		return props;
 	}

Modified: 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=1761786&r1=1761785&r2=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java (original)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedJPAEMFLocator.java Wed Sep 21 16:37:58 2016
@@ -28,6 +28,7 @@ import java.util.Hashtable;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.aries.tx.control.jpa.common.impl.AbstractJPAEntityManagerProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -45,17 +46,21 @@ public class ManagedJPAEMFLocator implem
 	private final String pid;
 	private final Map<String, Object> jpaProperties;
 	private final Map<String, Object> providerProperties;
+	private final Runnable onClose;
 	private final ServiceTracker<EntityManagerFactoryBuilder, EntityManagerFactoryBuilder> emfBuilderTracker;
 
-	private final AtomicReference<EntityManagerFactoryBuilder> activeDsf = new AtomicReference<>();
+	private final AtomicReference<EntityManagerFactoryBuilder> activeEMFB = new AtomicReference<>();
+	private final AtomicReference<AbstractJPAEntityManagerProvider> providerObject = 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 {
+			Map<String, Object> providerProperties, Runnable onClose) throws InvalidSyntaxException, ConfigurationException {
 		this.context = context;
 		this.pid = pid;
 		this.jpaProperties = jpaProperties;
 		this.providerProperties = providerProperties;
+		this.onClose = onClose;
 
 		String unitName = (String) providerProperties.get(JPA_UNIT_NAME);
 		if (unitName == null) {
@@ -93,13 +98,15 @@ public class ManagedJPAEMFLocator implem
 	private void updateService(EntityManagerFactoryBuilder service) {
 		boolean setEMFB;
 		synchronized (this) {
-			setEMFB = activeDsf.compareAndSet(null, service);
+			setEMFB = activeEMFB.compareAndSet(null, service);
 		}
 
 		if (setEMFB) {
+			AbstractJPAEntityManagerProvider provider = null;
 			try {
-				JPAEntityManagerProvider provider = new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
-						jpaProperties, providerProperties);
+				provider = new JPAEntityManagerProviderFactoryImpl().getProviderFor(service,
+						jpaProperties, providerProperties, onClose);
+				providerObject.set(provider);
 				ServiceRegistration<JPAEntityManagerProvider> reg = context
 						.registerService(JPAEntityManagerProvider.class, provider, getServiceProperties());
 				if (!serviceReg.compareAndSet(null, reg)) {
@@ -107,7 +114,11 @@ public class ManagedJPAEMFLocator implem
 				}
 			} catch (Exception e) {
 				ManagedServiceFactoryImpl.LOG.error("An error occurred when creating the connection provider for {}.", pid, e);
-				activeDsf.compareAndSet(service, null);
+				activeEMFB.compareAndSet(service, null);
+				if(provider != null) {
+					provider.close();
+				}
+					
 			}
 		}
 	}
@@ -125,11 +136,13 @@ public class ManagedJPAEMFLocator implem
 
 	@Override
 	public void removedService(ServiceReference<EntityManagerFactoryBuilder> reference, EntityManagerFactoryBuilder service) {
-		boolean dsfLeft;
+		boolean emfbLeft;
 		ServiceRegistration<JPAEntityManagerProvider> oldReg = null;
+		AbstractJPAEntityManagerProvider toClose = null;
 		synchronized (this) {
-			dsfLeft = activeDsf.compareAndSet(service, null);
-			if (dsfLeft) {
+			emfbLeft = activeEMFB.compareAndSet(service, null);
+			if (emfbLeft) {
+				toClose = providerObject.get();
 				oldReg = serviceReg.getAndSet(null);
 			}
 		}
@@ -141,13 +154,22 @@ public class ManagedJPAEMFLocator implem
 				ManagedServiceFactoryImpl.LOG.debug("An exception occurred when unregistering a service for {}", pid);
 			}
 		}
+		
+		if(toClose != null) {
+			try {
+				toClose.close();
+			} catch (Exception e) {
+				ManagedServiceFactoryImpl.LOG.debug("An Exception occured when closing the Resource provider for {}", pid, e);
+			}
+		}
+		
 		try {
 			context.ungetService(reference);
 		} catch (IllegalStateException ise) {
 			ManagedServiceFactoryImpl.LOG.debug("An exception occurred when ungetting the service for {}", reference);
 		}
 
-		if (dsfLeft) {
+		if (emfbLeft) {
 			EntityManagerFactoryBuilder newEMFBuilder = emfBuilderTracker.getService();
 			if (newEMFBuilder != null) {
 				updateService(newEMFBuilder);

Modified: 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=1761786&r1=1761785&r2=1761786&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java (original)
+++ aries/trunk/tx-control/tx-control-provider-jpa-local/src/main/java/org/apache/aries/tx/control/jpa/local/impl/ManagedServiceFactoryImpl.java Wed Sep 21 16:37:58 2016
@@ -103,7 +103,7 @@ public class ManagedServiceFactoryImpl i
 					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);
+				worker = new ManagedJPAEMFLocator(context, pid, jpaProps, propsMap, null);
 			}
 			ofNullable(managedInstances.put(pid, worker)).ifPresent(LifecycleAware::stop);
 			worker.start();