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 2010/08/06 16:59:50 UTC

svn commit: r983007 [1/2] - in /incubator/aries/trunk/jpa: jpa-container-context/ jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ jpa-container-itest/src/test/java/org/apache/aries/jpa/container/itest/ jpa-container-ite...

Author: timothyjward
Date: Fri Aug  6 14:59:49 2010
New Revision: 983007

URL: http://svn.apache.org/viewvc?rev=983007&view=rev
Log:
ARIES-374 - JPA Persistence container quiesce support

Added:
    incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPATest.java
      - copied, changed from r982543, incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPAContextTest.java
    incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CoundownCallback.java
    incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CountingEntityManagerFactory.java
    incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DestroyCallback.java
    incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerWrapper.java
    incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/QuiesceParticipantImpl.java
    incubator/aries/trunk/jpa/jpa-container/src/test/resources/testProps.props
Removed:
    incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPAContextTest.java
    incubator/aries/trunk/jpa/jpa-container/src/main/resources/OSGI-INF/
Modified:
    incubator/aries/trunk/jpa/jpa-container-context/pom.xml
    incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java
    incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/QuiesceParticipantImpl.java
    incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/container/itest/JPAContainerTest.java
    incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/context/itest/JPAContextTest.java
    incubator/aries/trunk/jpa/jpa-container/pom.xml
    incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerFactoryManager.java
    incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleManager.java
    incubator/aries/trunk/jpa/jpa-container/src/test/java/org/apache/aries/jpa/container/PersistenceBundleLifecycleTest.java

Modified: incubator/aries/trunk/jpa/jpa-container-context/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/pom.xml?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/pom.xml (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/pom.xml Fri Aug  6 14:59:49 2010
@@ -106,7 +106,7 @@
                             javax.persistence.criteria;version="[1.1.0,2.1.0)";resolution:=optional,
                             javax.persistence.metamodel;version="[1.1.0,2.1.0)";resolution:=optional,
                             org.apache.aries.jpa.container.context;version="[0.1.0,1.1.0)",
-                            org.apache.aries.quiesce.manager;version="[0.2,0.3)";resolution:=optional,
+                            org.apache.aries.quiesce.manager;version="[0.2,1.0)";resolution:=optional,
                             org.apache.aries.quiesce.participant;version="[0.2,0.3)";resolution:=optional,
                             *
                         </Import-Package>

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java Fri Aug  6 14:59:49 2010
@@ -56,7 +56,6 @@ public class ManagedPersistenceContextFa
   private final Map<String, Object> properties;
   private final JTAPersistenceContextRegistry registry;
   private final PersistenceContextType type;
-  private final AtomicBoolean quiesce = new AtomicBoolean(false);
   private final AtomicLong activeCount = new AtomicLong(0);
   private final String unitName;
   
@@ -126,8 +125,7 @@ public class ManagedPersistenceContextFa
    * @param tidyUp
    */
   public void quiesce(QuiesceTidyUp tidyUp) {
-    this.tidyUp.set(tidyUp);
-    quiesce.set(true);
+    this.tidyUp.compareAndSet(null, tidyUp);
     if(activeCount.get() == 0) {
       tidyUp.unitQuiesced(unitName);
     }
@@ -137,7 +135,7 @@ public class ManagedPersistenceContextFa
    * Quiesce this unit after the last context is destroyed
    */
   public void callback() {
-    if(quiesce.get() && activeCount.get() == 0) {
+    if(tidyUp.get() != null && activeCount.get() == 0) {
       tidyUp.get().unitQuiesced(unitName);
     }
   }

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/QuiesceParticipantImpl.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/QuiesceParticipantImpl.java?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/QuiesceParticipantImpl.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/QuiesceParticipantImpl.java Fri Aug  6 14:59:49 2010
@@ -19,6 +19,7 @@
 package org.apache.aries.jpa.container.context.impl;
 
 import java.util.List;
+import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionException;
@@ -102,6 +103,8 @@ public class QuiesceParticipantImpl impl
 	/** The Global manager for persistence contexts */
 	private final GlobalPersistenceManager mgr;
 	
+  /** Some events that we need to tidy up */
+  private final BlockingQueue<DestroyCallback> unhandledQuiesces = new LinkedBlockingQueue<DestroyCallback>();
 	
 	public QuiesceParticipantImpl(GlobalPersistenceManager mgr) {
     this.mgr = mgr;
@@ -114,7 +117,7 @@ public class QuiesceParticipantImpl impl
 		  try {
 		    executor.execute(new QuiesceBundle(qc, b, mgr));
 		  } catch (RejectedExecutionException re) {
-		    
+		    unhandledQuiesces.add(new QuiesceDelegatingCallback(qc, b));
 		  }
 		  //If we are quiescing, then we need to quiesce this threadpool!
 		  if(b.equals(mgr.getBundle()))
@@ -128,7 +131,9 @@ public class QuiesceParticipantImpl impl
   public void callback() {
     executor.shutdown();
     try {
-      executor.awaitTermination(10, TimeUnit.SECONDS);
+      for(DestroyCallback cbk : unhandledQuiesces)
+        cbk.callback();
+      executor.awaitTermination(5, TimeUnit.SECONDS);
     } catch (InterruptedException e) {
       //We don't care
     }

Modified: incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/container/itest/JPAContainerTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/container/itest/JPAContainerTest.java?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/container/itest/JPAContainerTest.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/container/itest/JPAContainerTest.java Fri Aug  6 14:59:49 2010
@@ -79,7 +79,6 @@ public class JPAContainerTest {
         // Bundles
         mavenBundle("org.osgi", "org.osgi.compendium"),
         mavenBundle("org.apache.aries", "org.apache.aries.util"),
-        mavenBundle("org.apache.aries.blueprint", "org.apache.aries.blueprint"), 
         mavenBundle("org.apache.geronimo.specs", "geronimo-jpa_2.0_spec"),
         mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.api"),
         mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.container"),

Modified: incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/context/itest/JPAContextTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/context/itest/JPAContextTest.java?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/context/itest/JPAContextTest.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/context/itest/JPAContextTest.java Fri Aug  6 14:59:49 2010
@@ -105,7 +105,6 @@ public class JPAContextTest {
         // Bundles
         mavenBundle("org.osgi", "org.osgi.compendium"),
         mavenBundle("org.apache.aries", "org.apache.aries.util"),
-        mavenBundle("org.apache.aries.blueprint", "org.apache.aries.blueprint"), 
         mavenBundle("org.apache.geronimo.specs", "geronimo-jpa_2.0_spec"),
         mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.api"),
         mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.container"),

Copied: incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPATest.java (from r982543, incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPAContextTest.java)
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPATest.java?p2=incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPATest.java&p1=incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPAContextTest.java&r1=982543&r2=983007&rev=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPAContextTest.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPATest.java Fri Aug  6 14:59:49 2010
@@ -29,6 +29,7 @@ import static org.ops4j.pax.exam.contain
 import java.util.Collections;
 import java.util.HashMap;
 
+import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.PersistenceContextType;
 import javax.transaction.UserTransaction;
@@ -58,7 +59,7 @@ import org.osgi.framework.Version;
 import org.osgi.util.tracker.ServiceTracker;
 
 @RunWith(JUnit4TestRunner.class)
-public class QuiesceJPAContextTest {
+public class QuiesceJPATest {
   
   private static class TestQuiesceCallback implements QuiesceCallback{
 
@@ -87,13 +88,17 @@ public class QuiesceJPAContextTest {
     b.stop();
     b.start();
     
+    b = getBundle("org.apache.aries.jpa.container");
+    b.stop();
+    b.start();
+    
     b = getBundle("org.apache.aries.jpa.container.context");
     b.stop();
     b.start();
   }
   
   @Test
-  public void testSimpleQuiesce() throws Exception {
+  public void testSimpleContextQuiesce() throws Exception {
 
     PersistenceContextProvider provider = getOsgiService(PersistenceContextProvider.class);
     
@@ -113,7 +118,7 @@ public class QuiesceJPAContextTest {
     participant.quiesce(callback, Collections.singletonList(getBundle(
         "org.apache.aries.jpa.org.apache.aries.jpa.container.itest.bundle")));
     
-    Thread.sleep(5000);
+    Thread.sleep(1000);
     
     assertTrue("Quiesce not finished", callback.bundleClearedUp());
     
@@ -125,7 +130,7 @@ public class QuiesceJPAContextTest {
   }
 
   @Test
-  public void testComplexQuiesce() throws Exception {
+  public void testComplexContextQuiesce() throws Exception {
 
     PersistenceContextProvider provider = getOsgiService(PersistenceContextProvider.class);
     
@@ -151,9 +156,9 @@ public class QuiesceJPAContextTest {
     participant.quiesce(callback, Collections.singletonList(getBundle(
         "org.apache.aries.jpa.org.apache.aries.jpa.container.itest.bundle")));
     
-    Thread.sleep(5000);
+    Thread.sleep(1000);
     
-    assertFalse("Quiesce not finished", callback.bundleClearedUp());
+    assertFalse("Quiesce finished", callback.bundleClearedUp());
     
     emf = getOsgiService(EntityManagerFactory.class, "(&(osgi.unit.name=test-unit)(" 
         + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true)" +
@@ -171,7 +176,7 @@ public class QuiesceJPAContextTest {
   }
   
   @Test
-  public void testRuntimeQuiesce() throws Exception {
+  public void testContextRuntimeQuiesce() throws Exception {
 
     PersistenceContextProvider provider = getOsgiService(PersistenceContextProvider.class);
     
@@ -197,7 +202,7 @@ public class QuiesceJPAContextTest {
     participant.quiesce(callback, Collections.singletonList(getBundle(
         "org.apache.aries.jpa.container.context")));
     
-    Thread.sleep(5000);
+    Thread.sleep(1000);
     
     assertFalse("Quiesce not finished", callback.bundleClearedUp());
     
@@ -216,6 +221,96 @@ public class QuiesceJPAContextTest {
     assertNull("No context should exist",refs);
   }
   
+  @Test
+  public void testSimpleUnitQuiesce() throws Exception {
+
+    
+    EntityManagerFactory emf = getOsgiService(EntityManagerFactory.class, "(&(osgi.unit.name=test-unit)(" 
+          + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))", DEFAULT_TIMEOUT);
+    
+    QuiesceParticipant participant = getParticipant("org.apache.aries.jpa.container");
+    
+    TestQuiesceCallback callback = new TestQuiesceCallback();
+    
+    participant.quiesce(callback, Collections.singletonList(getBundle(
+        "org.apache.aries.jpa.org.apache.aries.jpa.container.itest.bundle")));
+    
+    Thread.sleep(1000);
+    
+    assertTrue("Quiesce not finished", callback.bundleClearedUp());
+    
+    ServiceReference[] refs = bundleContext.getAllServiceReferences(EntityManagerFactory.class.getName(), "(&(osgi.unit.name=test-unit)(" 
+          + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))");
+    
+    assertNull("No context should exist",refs);
+  }
+
+  @Test
+  public void testComplexUnitQuiesce() throws Exception {
+
+    
+    EntityManagerFactory emf = getOsgiService(EntityManagerFactory.class, "(&(osgi.unit.name=test-unit)(" 
+          + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))", DEFAULT_TIMEOUT);
+    
+    EntityManager em = emf.createEntityManager();
+    
+    QuiesceParticipant participant = getParticipant("org.apache.aries.jpa.container");
+    
+    TestQuiesceCallback callback = new TestQuiesceCallback();
+    
+    participant.quiesce(callback, Collections.singletonList(getBundle(
+        "org.apache.aries.jpa.org.apache.aries.jpa.container.itest.bundle")));
+    
+    Thread.sleep(1000);
+    
+    assertFalse("Quiesce finished", callback.bundleClearedUp());
+    
+    emf = getOsgiService(EntityManagerFactory.class, "(&(osgi.unit.name=test-unit)(" 
+        + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))", DEFAULT_TIMEOUT);
+    
+    em.close();
+    
+    assertTrue("Quiesce not finished", callback.bundleClearedUp());
+    
+    ServiceReference[] refs = bundleContext.getAllServiceReferences(EntityManagerFactory.class.getName(), "(&(osgi.unit.name=test-unit)(" 
+          + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))");
+    
+    assertNull("No context should exist",refs);
+  }
+  
+  @Test
+  public void testContainerRuntimeQuiesce() throws Exception {
+    
+    EntityManagerFactory emf = getOsgiService(EntityManagerFactory.class, "(&(osgi.unit.name=test-unit)(" 
+          + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))", DEFAULT_TIMEOUT);
+    
+    
+    EntityManager em = emf.createEntityManager();
+    
+    QuiesceParticipant participant = getParticipant("org.apache.aries.jpa.container");
+    
+    TestQuiesceCallback callback = new TestQuiesceCallback();
+    
+    participant.quiesce(callback, Collections.singletonList(getBundle(
+        "org.apache.aries.jpa.container")));
+    
+    Thread.sleep(1000);
+    
+    assertFalse("Quiesce finished early", callback.bundleClearedUp());
+    
+    emf = getOsgiService(EntityManagerFactory.class, "(&(osgi.unit.name=test-unit)(" 
+        + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))", DEFAULT_TIMEOUT);
+
+    em.close();
+    
+    assertTrue("Quiesce not finished", callback.bundleClearedUp());
+    
+    ServiceReference[] refs = bundleContext.getAllServiceReferences(EntityManagerFactory.class.getName(), "(&(osgi.unit.name=test-unit)(" 
+          + PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true))");
+    
+    assertNull("No context should exist",refs);
+  }
+  
   private QuiesceParticipant getParticipant(String bundleName) throws InvalidSyntaxException {
     ServiceReference[] refs = bundleContext.getServiceReferences(QuiesceParticipant.class.getName(), null);
     

Modified: incubator/aries/trunk/jpa/jpa-container/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/pom.xml?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/pom.xml (original)
+++ incubator/aries/trunk/jpa/jpa-container/pom.xml Fri Aug  6 14:59:49 2010
@@ -27,6 +27,13 @@
     <artifactId>org.apache.aries.jpa.container</artifactId>
     <packaging>bundle</packaging>
     <name>Aries JPA Container</name>
+    
+    <properties>
+      <aries.osgi.activator>
+          org.apache.aries.jpa.container.impl.PersistenceBundleManager
+      </aries.osgi.activator>
+    </properties>
+    
     <dependencies>
         <dependency>
             <groupId>org.osgi</groupId>
@@ -43,6 +50,11 @@
             <groupId>org.apache.aries.jpa</groupId>
         </dependency>
         <dependency>
+            <groupId>org.apache.aries.quiesce</groupId>
+            <artifactId>org.apache.aries.quiesce.api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <artifactId>org.apache.aries.util</artifactId>
             <groupId>org.apache.aries</groupId>
             <scope>provided</scope>
@@ -90,6 +102,8 @@
                             javax.persistence.metamodel;version="[1.1.0,2.1.0)";resolution:=optional,
                             org.apache.aries.jpa.container;version="[0.1.0,1.1.0)",
                             org.apache.aries.jpa.container.parsing;version="[0.1.0,1.1.0)",
+                            org.apache.aries.quiesce.manager;version="[0.2,1.0)";resolution:=optional,
+                            org.apache.aries.quiesce.participant;version="[0.2,0.3)";resolution:=optional,
                             *
                         </Import-Package>
                         <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>

Added: incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CoundownCallback.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CoundownCallback.java?rev=983007&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CoundownCallback.java (added)
+++ incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CoundownCallback.java Fri Aug  6 14:59:49 2010
@@ -0,0 +1,39 @@
+/*
+ * 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.jpa.container.impl;
+
+/**
+ * A {@link DestroyCallback} that delegates after the correct
+ * number of calls 
+ */
+public final class CoundownCallback implements DestroyCallback {
+  private final DestroyCallback callback;
+  private int counter;
+
+  public CoundownCallback(int count,
+      DestroyCallback callback) {
+    this.callback = callback;
+    counter = count;
+  }
+
+  public void callback() {
+    if(--counter == 0)
+      callback.callback();
+  }
+}
\ No newline at end of file

Added: incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CountingEntityManagerFactory.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CountingEntityManagerFactory.java?rev=983007&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CountingEntityManagerFactory.java (added)
+++ incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/CountingEntityManagerFactory.java Fri Aug  6 14:59:49 2010
@@ -0,0 +1,116 @@
+/*
+ * 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.jpa.container.impl;
+
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.persistence.Cache;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.PersistenceUnitUtil;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.metamodel.Metamodel;
+
+import org.apache.aries.jpa.container.impl.EntityManagerFactoryManager.NamedCallback;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * An {@link EntityManagerFactory} that keeps track of the number of active instances
+ * so that it can be quiesced
+ */
+public class CountingEntityManagerFactory implements EntityManagerFactory, DestroyCallback {
+  /** Number of open EntityManagers */
+  private final AtomicLong count = new AtomicLong(0);
+  /** The real EMF */
+  private final EntityManagerFactory delegate;
+  /** The name of this unit */
+  private final String name;
+  /** A quiesce callback to call */
+  private final AtomicReference<NamedCallback> callback = new AtomicReference<NamedCallback>();
+  /** The service registration to unregister if we can quiesce */
+  private final AtomicReference<ServiceRegistration> reg = new AtomicReference<ServiceRegistration>();
+  
+  
+  public CountingEntityManagerFactory(
+      EntityManagerFactory containerEntityManagerFactory, String name) {
+    delegate = containerEntityManagerFactory;
+    this.name = name;
+  }
+
+  public void close() {
+    delegate.close();
+  }
+
+  public EntityManager createEntityManager() {
+    EntityManager em = delegate.createEntityManager();
+    count.incrementAndGet();
+    return new EntityManagerWrapper(em, this);
+  }
+
+  public EntityManager createEntityManager(Map arg0) {
+    EntityManager em = delegate.createEntityManager(arg0);
+    count.incrementAndGet();
+    return new EntityManagerWrapper(em, this);
+  }
+
+  public Cache getCache() {
+    return delegate.getCache();
+  }
+
+  public CriteriaBuilder getCriteriaBuilder() {
+    return delegate.getCriteriaBuilder();
+  }
+
+  public Metamodel getMetamodel() {
+    return delegate.getMetamodel();
+  }
+
+  public PersistenceUnitUtil getPersistenceUnitUtil() {
+    return delegate.getPersistenceUnitUtil();
+  }
+
+  public Map<String, Object> getProperties() {
+    return delegate.getProperties();
+  }
+
+  public boolean isOpen() {
+    return delegate.isOpen();
+  }
+
+  public void quiesce(NamedCallback callback, ServiceRegistration reg) {
+    this.reg.compareAndSet(null, reg);
+    this.callback.compareAndSet(null, callback);
+    if(count.get() == 0) {
+      PersistenceBundleManager.unregister(this.reg.getAndSet(null));
+      callback.callback(name);
+    }
+  }
+
+  public void callback() {
+    if(count.decrementAndGet() == 0 && callback.get() != null) {
+      PersistenceBundleManager.unregister(reg.getAndSet(null));
+      callback.get().callback(name);
+    }
+      
+  }
+
+}

Added: incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DestroyCallback.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DestroyCallback.java?rev=983007&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DestroyCallback.java (added)
+++ incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DestroyCallback.java Fri Aug  6 14:59:49 2010
@@ -0,0 +1,26 @@
+/*
+ * 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.jpa.container.impl;
+
+/**
+ * An asynchronous callback for destroying something
+ */
+public interface DestroyCallback {
+  public void callback();
+}

Modified: incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerFactoryManager.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerFactoryManager.java?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerFactoryManager.java (original)
+++ incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerFactoryManager.java Fri Aug  6 14:59:49 2010
@@ -21,9 +21,13 @@ package org.apache.aries.jpa.container.i
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.spi.PersistenceProvider;
@@ -44,6 +48,27 @@ import org.slf4j.LoggerFactory;
  */
 public class EntityManagerFactoryManager {
 
+  /**
+   * A callback for a named persistence units
+   */
+  class NamedCallback {
+    private final Set<String> names;
+    private final DestroyCallback callback;
+    public NamedCallback(Collection<String> names, DestroyCallback countdown) {
+      this.names = new HashSet<String>(names);
+      callback = countdown;
+    }
+
+    public void callback(String name) {
+      boolean winner;
+      synchronized (this) {
+        winner = !!!names.isEmpty() && names.remove(name) && names.isEmpty();
+      }
+      if(winner)
+        callback.callback();
+    }
+  }
+
   /** The container's {@link BundleContext} */
   private final BundleContext containerContext;
   /** The persistence bundle */
@@ -55,9 +80,11 @@ public class EntityManagerFactoryManager
   /** The original parsed data */
   private Collection<ParsedPersistenceUnit> parsedData;
   /** A Map of created {@link EntityManagerFactory}s */
-  private Map<String, EntityManagerFactory> emfs = null;
+  private Map<String, CountingEntityManagerFactory> emfs = null;
   /** The {@link ServiceRegistration} objects for the {@link EntityManagerFactory}s */
-  private Collection<ServiceRegistration> registrations = null;
+  private ConcurrentMap<String, ServiceRegistration> registrations = null;
+  /** Quiesce this Manager */
+  private boolean quiesce = false;
 
   /** Logger */
   private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container");
@@ -98,7 +125,7 @@ public class EntityManagerFactoryManager
    */
   public synchronized boolean providerRemoved(ServiceReference ref) {
     
-    boolean toReturn = ref == provider;
+    boolean toReturn = provider.equals(ref);
     
     if(toReturn)
       destroy();
@@ -146,7 +173,7 @@ public class EntityManagerFactoryManager
   private void unregisterEntityManagerFactories() {
     //If we have registrations then unregister them
     if(registrations != null) {
-      for(ServiceRegistration reg : registrations) {
+      for(ServiceRegistration reg : registrations.values()) {
         try {
           reg.unregister();
         } catch (Exception e) {
@@ -168,11 +195,11 @@ public class EntityManagerFactoryManager
   private void registerEntityManagerFactories() throws InvalidPersistenceUnitException {
     //Only register if there is a provider and we are not
     //already registered
-    if(provider != null && registrations == null) {
+    if(provider != null && registrations == null && !quiesce) {
       //Make sure the EntityManagerFactories are instantiated
       createEntityManagerFactories();
       
-      registrations = new ArrayList<ServiceRegistration>();
+      registrations = new ConcurrentHashMap<String, ServiceRegistration>();
       String providerName = (String) provider.getProperty("javax.persistence.provider");
       if(providerName == null) {
         _logger.warn("The PersistenceProvider for bundle {} did not specify a provider name in the \"javax.persistence.provider\" service property. " +
@@ -182,7 +209,7 @@ public class EntityManagerFactoryManager
             new Object[] {bundle.getSymbolicName() + "_" + bundle.getVersion(), provider});
       }
       //Register each EMF
-      for(Entry<String, EntityManagerFactory> entry : emfs.entrySet())
+      for(Entry<String, ? extends EntityManagerFactory> entry : emfs.entrySet())
       {
         Properties props = new Properties();
         String unitName = entry.getKey();
@@ -194,7 +221,7 @@ public class EntityManagerFactoryManager
         props.put(PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT, Boolean.TRUE);
         props.put(PersistenceUnitConstants.EMPTY_PERSISTENCE_UNIT_NAME, "".equals(unitName));
         try {
-          registrations.add(bundle.getBundleContext().registerService(EntityManagerFactory.class.getCanonicalName(), entry.getValue(), props));
+          registrations.put(unitName, bundle.getBundleContext().registerService(EntityManagerFactory.class.getCanonicalName(), entry.getValue(), props));
         } catch (Exception e) {
           _logger.error("There was an error registering the persistence unit " 
               + unitName + " defined by the bundle " + bundle.getSymbolicName() + "_" + bundle.getVersion(), e);
@@ -212,9 +239,9 @@ public class EntityManagerFactoryManager
   private void createEntityManagerFactories() throws InvalidPersistenceUnitException {
     //Only try if we have a provider and EMFs
     if(provider != null) {
-      if(emfs == null) {
+      if(emfs == null && !quiesce) {
         try {
-          emfs = new HashMap<String, EntityManagerFactory>();
+          emfs = new HashMap<String, CountingEntityManagerFactory>();
         
           //Get hold of the provider
           PersistenceProvider providerService = (PersistenceProvider) containerContext.getService(provider);
@@ -229,9 +256,9 @@ public class EntityManagerFactoryManager
           for(ManagedPersistenceUnitInfo info : persistenceUnits){
             PersistenceUnitInfo pUnitInfo = info.getPersistenceUnitInfo();
         
-            emfs.put(pUnitInfo.getPersistenceUnitName(), 
+            emfs.put(pUnitInfo.getPersistenceUnitName(), new CountingEntityManagerFactory(
                 providerService.createContainerEntityManagerFactory(
-                    pUnitInfo, info.getContainerProperties()));
+                    pUnitInfo, info.getContainerProperties()), pUnitInfo.getPersistenceUnitName()));
           }
         } finally {
           //Remember to unget the provider
@@ -292,7 +319,7 @@ public class EntityManagerFactoryManager
     if(registrations != null)
       unregisterEntityManagerFactories();
     if(emfs != null) {
-      for(Entry<String, EntityManagerFactory> entry : emfs.entrySet()) {
+      for(Entry<String, ? extends EntityManagerFactory> entry : emfs.entrySet()) {
         try {
           entry.getValue().close();
         } catch (Exception e) {
@@ -312,5 +339,31 @@ public class EntityManagerFactoryManager
   {
     return parsedData;
   }
+  /** Quiesce this Manager */
+  public void quiesce(DestroyCallback countdown) {
+    
+    //Find the EMFs to quiesce, and their Service registrations
+    Map<CountingEntityManagerFactory, ServiceRegistration> entries = new HashMap<CountingEntityManagerFactory, ServiceRegistration>();
+    Collection<String> names = new ArrayList<String>();
+    synchronized(this) {
+      quiesce = true;
+      if(emfs != null) {
+        for(String key : emfs.keySet()) {
+          entries.put(emfs.get(key), registrations != null ? registrations.get(key) : null);
+          names.add(key);
+        }
+      }
+    }
+    //Quiesce as necessary
+    if(entries.isEmpty())
+      countdown.callback();
+    else {
+    NamedCallback callback = new NamedCallback(names, countdown);
+      for(Entry<CountingEntityManagerFactory, ServiceRegistration> entry : entries.entrySet()) {
+        CountingEntityManagerFactory emf = entry.getKey();
+        emf.quiesce(callback, entry.getValue());
+      }
+    }
+  }
 
 }

Added: incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerWrapper.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerWrapper.java?rev=983007&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerWrapper.java (added)
+++ incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/EntityManagerWrapper.java Fri Aug  6 14:59:49 2010
@@ -0,0 +1,211 @@
+/*
+ * 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.jpa.container.impl;
+
+import java.util.Map;
+
+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.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.metamodel.Metamodel;
+
+/**
+ * Wrapper an EntityManager so that we know when it has been closed
+ */
+public class EntityManagerWrapper implements EntityManager {
+  
+  private final EntityManager delegate;
+  /** Call this when the EntityManager is closed */
+  private final DestroyCallback callback;
+
+  public EntityManagerWrapper(EntityManager em, DestroyCallback callback) {
+    delegate = em;
+    this.callback = callback;
+  }
+
+  public void clear() {
+    delegate.clear();
+  }
+
+  public void close() {
+    delegate.close();
+    //This will only ever be called once, the second time there
+    //will be an IllegalStateException from the line above
+    callback.callback();
+  }
+
+  public boolean contains(Object arg0) {
+    return delegate.contains(arg0);
+  }
+
+  public <T> TypedQuery<T> createNamedQuery(String arg0, Class<T> arg1) {
+    return delegate.createNamedQuery(arg0, arg1);
+  }
+
+  public Query createNamedQuery(String arg0) {
+    return delegate.createNamedQuery(arg0);
+  }
+
+  public Query createNativeQuery(String arg0, Class arg1) {
+    return delegate.createNativeQuery(arg0, arg1);
+  }
+
+  public Query createNativeQuery(String arg0, String arg1) {
+    return delegate.createNativeQuery(arg0, arg1);
+  }
+
+  public Query createNativeQuery(String arg0) {
+    return delegate.createNativeQuery(arg0);
+  }
+
+  public <T> TypedQuery<T> createQuery(CriteriaQuery<T> arg0) {
+    return delegate.createQuery(arg0);
+  }
+
+  public <T> TypedQuery<T> createQuery(String arg0, Class<T> arg1) {
+    return delegate.createQuery(arg0, arg1);
+  }
+
+  public Query createQuery(String arg0) {
+    return delegate.createQuery(arg0);
+  }
+
+  public void detach(Object arg0) {
+    delegate.detach(arg0);
+  }
+
+  public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2,
+      Map<String, Object> arg3) {
+    return delegate.find(arg0, arg1, arg2, arg3);
+  }
+
+  public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2) {
+    return delegate.find(arg0, arg1, arg2);
+  }
+
+  public <T> T find(Class<T> arg0, Object arg1, Map<String, Object> arg2) {
+    return delegate.find(arg0, arg1, arg2);
+  }
+
+  public <T> T find(Class<T> arg0, Object arg1) {
+    return delegate.find(arg0, arg1);
+  }
+
+  public void flush() {
+    delegate.flush();
+  }
+
+  public CriteriaBuilder getCriteriaBuilder() {
+    return delegate.getCriteriaBuilder();
+  }
+
+  public Object getDelegate() {
+    return delegate.getDelegate();
+  }
+
+  public EntityManagerFactory getEntityManagerFactory() {
+    return delegate.getEntityManagerFactory();
+  }
+
+  public FlushModeType getFlushMode() {
+    return delegate.getFlushMode();
+  }
+
+  public LockModeType getLockMode(Object arg0) {
+    return delegate.getLockMode(arg0);
+  }
+
+  public Metamodel getMetamodel() {
+    return delegate.getMetamodel();
+  }
+
+  public Map<String, Object> getProperties() {
+    return delegate.getProperties();
+  }
+
+  public <T> T getReference(Class<T> arg0, Object arg1) {
+    return delegate.getReference(arg0, arg1);
+  }
+
+  public EntityTransaction getTransaction() {
+    return delegate.getTransaction();
+  }
+
+  public boolean isOpen() {
+    return delegate.isOpen();
+  }
+
+  public void joinTransaction() {
+    delegate.joinTransaction();
+  }
+
+  public void lock(Object arg0, LockModeType arg1, Map<String, Object> arg2) {
+    delegate.lock(arg0, arg1, arg2);
+  }
+
+  public void lock(Object arg0, LockModeType arg1) {
+    delegate.lock(arg0, arg1);
+  }
+
+  public <T> T merge(T arg0) {
+    return delegate.merge(arg0);
+  }
+
+  public void persist(Object arg0) {
+    delegate.persist(arg0);
+  }
+
+  public void refresh(Object arg0, LockModeType arg1, Map<String, Object> arg2) {
+    delegate.refresh(arg0, arg1, arg2);
+  }
+
+  public void refresh(Object arg0, LockModeType arg1) {
+    delegate.refresh(arg0, arg1);
+  }
+
+  public void refresh(Object arg0, Map<String, Object> arg1) {
+    delegate.refresh(arg0, arg1);
+  }
+
+  public void refresh(Object arg0) {
+    delegate.refresh(arg0);
+  }
+
+  public void remove(Object arg0) {
+    delegate.remove(arg0);
+  }
+
+  public void setFlushMode(FlushModeType arg0) {
+    delegate.setFlushMode(arg0);
+  }
+
+  public void setProperty(String arg0, Object arg1) {
+    delegate.setProperty(arg0, arg1);
+  }
+
+  public <T> T unwrap(Class<T> arg0) {
+    return delegate.unwrap(arg0);
+  }
+}

Modified: incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleManager.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleManager.java?rev=983007&r1=983006&r2=983007&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleManager.java (original)
+++ incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleManager.java Fri Aug  6 14:59:49 2010
@@ -33,36 +33,48 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.persistence.spi.PersistenceProvider;
 
-import org.apache.aries.util.VersionRange;
 import org.apache.aries.jpa.container.ManagedPersistenceUnitInfo;
 import org.apache.aries.jpa.container.ManagedPersistenceUnitInfoFactory;
 import org.apache.aries.jpa.container.parsing.ParsedPersistenceUnit;
 import org.apache.aries.jpa.container.parsing.PersistenceDescriptor;
 import org.apache.aries.jpa.container.parsing.PersistenceDescriptorParser;
 import org.apache.aries.jpa.container.parsing.PersistenceDescriptorParserException;
+import org.apache.aries.jpa.container.parsing.impl.PersistenceDescriptorParserImpl;
 import org.apache.aries.jpa.container.unit.impl.ManagedPersistenceUnitInfoFactoryImpl;
+import org.apache.aries.util.VersionRange;
 import org.apache.aries.util.tracker.RecursiveBundleTracker;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.Version;
 import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * This class locates, parses and manages persistence units defined in OSGi bundles.
  */
-public class PersistenceBundleManager implements BundleTrackerCustomizer
+public class PersistenceBundleManager implements BundleTrackerCustomizer, ServiceTrackerCustomizer, BundleActivator
 {
+  /** The QuiesceParticipant implementation class name */
+  private static final String QUIESCE_PARTICIPANT_CLASS = "org.apache.aries.quiesce.participant.QuiesceParticipant";
+  
   /** Logger */
   private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container");
   
   /** The bundle context for this bundle */
   private BundleContext ctx = null;
+
   /** 
    * A map of providers to persistence bundles this is used to guarantee that 
    * when a provider service is removed we can access all of the bundles that
@@ -85,33 +97,22 @@ public class PersistenceBundleManager im
   private ManagedPersistenceUnitInfoFactory persistenceUnitFactory; 
   /** Parser for persistence descriptors */
   private PersistenceDescriptorParser parser;
+  /** Registration for the Parser */
+  private ServiceRegistration parserReg;
   /** Configuration for this extender */
   private Properties config;
-  private final RecursiveBundleTracker tracker;
-
-  /**
-   * Create the extender. Note that it will not start tracking 
-   * until the {@code open()} method is called
-   * @param ctx The extender bundle's context
-   */
-  public PersistenceBundleManager(BundleContext ctx) 
-  {
-    this.ctx = ctx;
-    tracker = new RecursiveBundleTracker(ctx, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING |
-        Bundle.ACTIVE | Bundle.STOPPING, this);
-  }
-
-  /**
-   * Provide a parser implementation
-   * @param parser
-   */
-  public void setParser(PersistenceDescriptorParser descriptorParser) {
-    parser = descriptorParser;
-  }
+  private RecursiveBundleTracker tracker;
+  private ServiceTracker serviceTracker;
 
+  /** The quiesce participant service */
+  private ServiceRegistration quiesceReg;
+  /** A callback to shutdown the quiesce participant when it is done */
+  private DestroyCallback quiesceParticipant;
+  /** Are we quiescing */
+  private AtomicBoolean quiesce = new AtomicBoolean(false);
   
   @SuppressWarnings("unchecked")
-  public void open() {
+  private void open() {
     //Create the pluggable ManagedPersistenceUnitInfoFactory
     String className = config.getProperty(ManagedPersistenceUnitInfoFactory.DEFAULT_PU_INFO_FACTORY_KEY);
     
@@ -127,28 +128,38 @@ public class PersistenceBundleManager im
     
     if(persistenceUnitFactory == null)
       persistenceUnitFactory = new ManagedPersistenceUnitInfoFactoryImpl();
-    
+     serviceTracker.open();
      tracker.open();
   }
   
-  public void close()
+  private void close()
   {
     if (tracker != null) {
       tracker.close();
     }
+    
+    if (serviceTracker != null) {
+      serviceTracker.close();
+    }
   } 
   public Object addingBundle(Bundle bundle, BundleEvent event) 
   {
-    EntityManagerFactoryManager mgr = setupManager(bundle, null, true);
-    return mgr;
+    if(!!!quiesce.get()){
+      return setupManager(bundle, null, true);
+    }
+    return null;
   }
 
   /**
    * A provider is being added, add it to our Set
    * @param ref
    */
-  public void addingProvider(ServiceReference ref)
+  public Object addingService(ServiceReference ref)
   {
+    if(quiesce.get()){
+      return null;
+    }
+    
     Map<EntityManagerFactoryManager, ServiceReference> managersToManage = new HashMap<EntityManagerFactoryManager, ServiceReference>();
     synchronized (this) {
       if(_logger.isDebugEnabled())
@@ -193,6 +204,7 @@ public class PersistenceBundleManager im
         setupManager(mgr.getBundle(), mgr, false);
       }
     }
+    return ref;
   }
   
   /**
@@ -200,12 +212,8 @@ public class PersistenceBundleManager im
    * managers that it has been removed
    * @param ref
    */
-  public void removingProvider(ServiceReference ref)
-  {
-    //We may get a null reference if the ref-list is empty to start with
-    if(ref == null)
-      return;
-    
+  public void removedService(ServiceReference ref, Object o)
+  {    
     if(_logger.isDebugEnabled())
       _logger.debug("Removing a provider: {}", new Object[] {ref});
     
@@ -232,8 +240,8 @@ public class PersistenceBundleManager im
    * and override the supplied properties
    * @param props
    */
-  public void setConfig(Properties props) {
-    config = new Properties(props);
+  private void initConfig() {
+    config = new Properties();
     URL u = ctx.getBundle().getResource(ManagedPersistenceUnitInfoFactory.ARIES_JPA_CONTAINER_PROPERTIES);
     
     if(u != null) {
@@ -587,4 +595,111 @@ public class PersistenceBundleManager im
     _logger.warn("The persistence units for bundle " + bundle.getSymbolicName() + "_" + bundle.getVersion()
         + " became invalid and will be destroyed.", e);
   }
+
+  
+  public void modifiedService(ServiceReference reference, Object service) {
+    //Just remove and re-add as the properties have changed
+    removedService(reference, service);
+    addingService(reference);
+  }
+
+
+  public void start(BundleContext context) throws Exception {
+    
+    ctx = context;
+    
+    initConfig();
+    initParser();
+    
+    serviceTracker = new ServiceTracker(ctx, PersistenceProvider.class.getName(), this);
+    
+    tracker = new RecursiveBundleTracker(ctx, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING |
+        Bundle.ACTIVE | Bundle.STOPPING, this);
+    
+    open();
+    
+    try{
+      context.getBundle().loadClass(QUIESCE_PARTICIPANT_CLASS);
+      //Class was loaded, register
+      quiesceParticipant = new QuiesceParticipantImpl(this);
+      quiesceReg = context.registerService(QUIESCE_PARTICIPANT_CLASS,
+         quiesceParticipant, null);
+    } catch (ClassNotFoundException e) {
+      _logger.info("No quiesce support is available, so persistence contexts will not participate in quiesce operations", e);
+    }
+  }
+
+  private void initParser() {
+    parser = new PersistenceDescriptorParserImpl();
+    parserReg = ctx.registerService(PersistenceDescriptorParser.class.getName(), parser, null);
+  }
+
+  public void stop(BundleContext context) throws Exception {
+    close();
+    unregister(parserReg);
+    unregister(quiesceReg);
+    quiesceParticipant.callback();
+  }
+  
+  /**
+   * Clean up a registration without throwing an exception
+   * @param reg
+   */
+  static void unregister(ServiceRegistration reg) {
+    if(reg != null)
+      try {
+        reg.unregister();
+      } catch (IllegalStateException ise) {
+        //we don't care
+      }
+  }
+  
+  public BundleContext getCtx() {
+    return ctx;
+  }
+
+  public void quiesceBundle(Bundle bundleToQuiesce, final DestroyCallback callback) {
+    
+    boolean thisBundle =  bundleToQuiesce.equals(ctx.getBundle());
+    
+    if(thisBundle) {
+      quiesce.compareAndSet(false, true);
+      unregister(quiesceReg);
+    }
+    
+    Collection<EntityManagerFactoryManager> toDestroyNow = new ArrayList<EntityManagerFactoryManager>();
+    final Collection<EntityManagerFactoryManager> quiesceNow = new ArrayList<EntityManagerFactoryManager>();
+    synchronized (this) {
+      if(thisBundle) {
+        toDestroyNow.addAll(managersAwaitingProviders);
+        managersAwaitingProviders.clear();
+        quiesceNow.addAll(bundleToManagerMap.values());
+        bundleToManagerMap.clear();
+        quiesceNow.removeAll(toDestroyNow);
+      } else {
+        EntityManagerFactoryManager emfm = bundleToManagerMap.get(bundleToQuiesce);
+        
+        if(emfm != null){
+          if(managersAwaitingProviders.remove(emfm))
+            toDestroyNow.add(emfm);
+          else
+            quiesceNow.add(emfm);
+        }
+      }
+    }
+    
+    for(EntityManagerFactoryManager emfm : toDestroyNow)
+      emfm.destroy();
+    
+    if(quiesceNow.isEmpty()) {
+      callback.callback();
+    } else {
+      DestroyCallback countdown = new CoundownCallback(quiesceNow.size(), callback);
+      
+      for(EntityManagerFactoryManager emfm : quiesceNow)
+        emfm.quiesce(countdown);
+      
+    }
+    
+  }
 }

Added: incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/QuiesceParticipantImpl.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/QuiesceParticipantImpl.java?rev=983007&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/QuiesceParticipantImpl.java (added)
+++ incubator/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/QuiesceParticipantImpl.java Fri Aug  6 14:59:49 2010
@@ -0,0 +1,142 @@
+/*
+ * 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.jpa.container.impl;
+
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.aries.quiesce.manager.QuiesceCallback;
+import org.apache.aries.quiesce.participant.QuiesceParticipant;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class provides Quiesce Participant support for JPA managed units. It is the only
+ * class in this bundle that depends on the Quiesce API to make sure that the bundle can
+ * optionally depend on the API. If no Quiesce API is available then this class will not be
+ * loaded and no Quiesce support will be available.
+ */
+public class QuiesceParticipantImpl implements QuiesceParticipant, DestroyCallback {
+  
+  /**
+   * A wrapper to protect our internals from the Quiesce API so that we can make it
+   * an optional dependency
+   */
+  private static final class QuiesceDelegatingCallback implements DestroyCallback {
+    
+    /** The callback to delegate to */
+    private final QuiesceCallback callback;
+    
+    /** The single bundle being quiesced by this DestroyCallback */
+    private final Bundle toQuiesce;
+
+    public QuiesceDelegatingCallback(QuiesceCallback cbk, Bundle b) {
+      callback = cbk;
+      toQuiesce = b;
+    }
+    
+    public void callback() {
+      callback.bundleQuiesced(toQuiesce);
+    }
+    
+  }
+  
+  /**
+   * A runnable Quiesce operation for a single bundle
+   */
+  private static final class QuiesceBundle implements Runnable {
+    
+    /** The callback when we're done */
+    private final DestroyCallback callback;
+    /** The bundle being quiesced */
+    private final Bundle bundleToQuiesce;
+    /** The {@link PersistenceBundleManager} instance */
+    private final PersistenceBundleManager mgr; 
+
+    public QuiesceBundle(QuiesceCallback callback, Bundle bundleToQuiesce,
+        PersistenceBundleManager mgr) {
+      super();
+      this.callback = new QuiesceDelegatingCallback(callback, bundleToQuiesce);
+      this.bundleToQuiesce = bundleToQuiesce;
+      this.mgr = mgr;
+    }
+
+    public void run() {
+      mgr.quiesceBundle(bundleToQuiesce, callback);
+    }
+  }
+
+  /**
+   * A Threadpool for running quiesce operations
+   */
+  private final ExecutorService executor = new ThreadPoolExecutor(0, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
+    
+    public Thread newThread(Runnable r) {
+      Thread t = new Thread(r, "JPA-Container-ThreadPool");
+      t.setDaemon(true);
+      return t;
+    }
+  });
+  
+  /** The manager for persistence bundles */
+  private final PersistenceBundleManager mgr;
+  
+  /** Some events that we need to tidy up */
+  private final BlockingQueue<DestroyCallback> unhandledQuiesces = new LinkedBlockingQueue<DestroyCallback>(); 
+  
+  public QuiesceParticipantImpl(PersistenceBundleManager mgr) {
+    this.mgr = mgr;
+  }
+
+
+  public void quiesce(QuiesceCallback qc, List<Bundle> arg1) {
+    //Run a quiesce operation for each bundle being quiesced
+    for(Bundle b : arg1) {
+      try {
+        executor.execute(new QuiesceBundle(qc, b, mgr));
+      } catch (RejectedExecutionException re) {
+        unhandledQuiesces.add(new QuiesceDelegatingCallback(qc, b));
+      }
+      //If we are quiescing, then we need to quiesce this threadpool!
+      if(b.equals(mgr.getCtx().getBundle()))
+        executor.shutdown();
+    }
+  }
+  
+  /**
+   * Close down this object
+   */
+  public void callback() {
+    executor.shutdown();
+    try {
+      for(DestroyCallback cbk : unhandledQuiesces) {
+        cbk.callback();
+      }
+      executor.awaitTermination(5, TimeUnit.SECONDS);
+    } catch (InterruptedException e) {
+      //We don't care
+    }
+    executor.shutdownNow();
+  }
+}