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();



Reply via email to