incubator-aries-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From timothyjw...@apache.org
Subject svn commit: r982543 [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-context/src/main/java/org/apache/aries/jpa/container/context/transaction...
Date Thu, 05 Aug 2010 11:13:56 GMT
Author: timothyjward
Date: Thu Aug  5 11:13:55 2010
New Revision: 982543

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

Added:
    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-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/DestroyCallback.java
    incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/TranSyncRegistryMock.java
    incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/
    incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/
    incubator/aries/trunk/jpa/jpa-container-itest/src/test/java/org/apache/aries/jpa/quiesce/itest/QuiesceJPAContextTest.java
    incubator/aries/trunk/jpa/jpa-container-testbundle/src/main/resources/META-INF/
    incubator/aries/trunk/jpa/jpa-container-testbundle/src/main/resources/META-INF/persistence.xml
Removed:
    incubator/aries/trunk/jpa/jpa-container-context/src/main/resources/OSGI-INF/
    incubator/aries/trunk/jpa/jpa-container-testbundle/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/GlobalPersistenceManager.java
    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/PersistenceContextManager.java
    incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java
    incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java
    incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManagerTest.java
    incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java
    incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java
    incubator/aries/trunk/jpa/jpa-container-itest/pom.xml
    incubator/aries/trunk/jpa/jpa-container-testbundle/pom.xml
    incubator/aries/trunk/jpa/pom.xml

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=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/pom.xml (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/pom.xml Thu Aug  5 11:13:55 2010
@@ -28,6 +28,13 @@
     <name>Aries JPA Container Managed Contexts</name>
     <packaging>bundle</packaging>
 
+    <properties>
+      <aries.osgi.activator>
+          org.apache.aries.jpa.container.context.impl.GlobalPersistenceManager
+      </aries.osgi.activator>
+    
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.osgi</groupId>
@@ -60,6 +67,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.aries.quiesce</groupId>
+            <artifactId>org.apache.aries.quiesce.api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
@@ -94,6 +106,8 @@
                             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.participant;version="[0.2,0.3)";resolution:=optional,
                             *
                         </Import-Package>
                         <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManager.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManager.java?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManager.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManager.java Thu Aug  5 11:13:55 2010
@@ -18,15 +18,22 @@
  */
 package org.apache.aries.jpa.container.context.impl;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.Map.Entry;
 
 import org.apache.aries.jpa.container.context.PersistenceContextProvider;
+import org.apache.aries.jpa.container.context.transaction.impl.DestroyCallback;
 import org.apache.aries.jpa.container.context.transaction.impl.JTAPersistenceContextRegistry;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.SynchronousBundleListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -34,7 +41,10 @@ import org.slf4j.LoggerFactory;
 /**
  * Class that coordinates PersistenceContextManagers across multiple (nested) OSGi frameworks.
  */
-public class GlobalPersistenceManager implements PersistenceContextProvider, SynchronousBundleListener {
+public class GlobalPersistenceManager implements PersistenceContextProvider, SynchronousBundleListener, 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.context");
   
@@ -54,12 +64,14 @@ public class GlobalPersistenceManager im
    */
   private Map<Bundle, Set<String>> persistenceContexts = 
     new HashMap<Bundle, Set<String>>();
-  
 
-  public void setRegistry(JTAPersistenceContextRegistry registry) {
-    this.registry = registry;
-  }
-  
+  /** The registration for the quiesce participant */
+  private ServiceRegistration quiesceReg;
+  /** The registration for the persistence context manager */
+  private ServiceRegistration pcpReg;
+  /** Our bundle */
+  private Bundle bundle;
+   
   public void registerContext(String unitName, Bundle client, HashMap<String, Object> properties) {
     if (_logger.isDebugEnabled()) {
       _logger.debug("Registering bundle {} as a client of persistence unit {} with properties {}.", 
@@ -167,4 +179,107 @@ public class GlobalPersistenceManager im
     }
   }
 
+  /**
+   * Quiesce the supplied bundle
+   * @param bundleToQuiesce
+   * @param callback
+   */
+  public void quiesceBundle(Bundle bundleToQuiesce, final DestroyCallback callback) {
+    //If this is our bundle then get all the managers to clean up everything
+    if(bundleToQuiesce == bundle) {
+      unregister(pcpReg);
+      pcpReg = null;
+      final Collection<Entry<Bundle, PersistenceContextManager>> mgrs = new ArrayList<Entry<Bundle, PersistenceContextManager>>();
+      synchronized (this) {
+        mgrs.addAll(managers.entrySet());
+      }
+
+      if(managers.isEmpty())
+        callback.callback();
+      else {
+        final GlobalPersistenceManager gpm = this;
+        //A special callback to say we're done when everyone has replied!
+        DestroyCallback destroy = new DestroyCallback() {
+          int count = mgrs.size();
+          public void callback() {
+            count--;
+            if(count == 0) {
+              for(Entry<Bundle, PersistenceContextManager> entry : mgrs){
+                BundleContext ctx = entry.getKey().getBundleContext();
+                if(ctx != null) {
+                  ctx.removeBundleListener(gpm);
+                }
+              }
+              callback.callback();
+            }
+          }
+        };
+        for (Entry<Bundle, PersistenceContextManager> entry : mgrs)
+          entry.getValue().quiesceAllUnits(destroy);
+      }
+    } else {
+      //Just quiesce the one bundle
+      Bundle frameworkBundle = bundleToQuiesce.getBundleContext().getBundle(0);
+      PersistenceContextManager mgr = managers.get(frameworkBundle);
+    
+      //If there is no manager for this framework, then we aren't managing the bundle
+      //and can stop now
+      if(mgr == null) {
+        callback.callback();
+      } else {
+        mgr.quiesceUnits(bundleToQuiesce, callback);
+      }
+    }
+  }
+
+  public void start(BundleContext context) throws Exception {
+    
+    bundle = context.getBundle();
+    registry = new JTAPersistenceContextRegistry(context);
+    
+    //Register our service
+    pcpReg = context.registerService(PersistenceContextProvider.class.getName(), this, null);
+    
+    try{
+      context.getBundle().loadClass(QUIESCE_PARTICIPANT_CLASS);
+      //Class was loaded, register
+      
+      quiesceReg = context.registerService(QUIESCE_PARTICIPANT_CLASS,
+          new QuiesceParticipantImpl(this), null);
+    } catch (ClassNotFoundException e) {
+      _logger.info("No quiesce support is available, so persistence contexts will not participate in quiesce operations", e);
+    }
+  }
+
+  public void stop(BundleContext context) throws Exception {
+    //Clean up
+    unregister(pcpReg);
+    unregister(quiesceReg);
+    Collection<PersistenceContextManager> mgrs = new ArrayList<PersistenceContextManager>();
+    synchronized (persistenceContexts) {
+      mgrs.addAll(managers.values());
+    }
+    
+    for(PersistenceContextManager mgr : mgrs)
+      mgr.close();
+    
+    registry.close();
+  }
+
+  /**
+   * Clean up a registration without throwing an exception
+   * @param reg
+   */
+  private void unregister(ServiceRegistration reg) {
+    if(reg != null)
+      try {
+        reg.unregister();
+      } catch (IllegalStateException ise) {
+        //we don't care
+      }
+  }
+  
+  public Bundle getBundle() {
+    return bundle;
+  }
 }

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=982543&r1=982542&r2=982543&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 Thu Aug  5 11:13:55 2010
@@ -20,7 +20,9 @@ package org.apache.aries.jpa.container.c
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.persistence.Cache;
 import javax.persistence.EntityManager;
@@ -31,6 +33,8 @@ import javax.persistence.criteria.Criter
 import javax.persistence.metamodel.Metamodel;
 
 import org.apache.aries.jpa.container.context.PersistenceContextProvider;
+import org.apache.aries.jpa.container.context.impl.PersistenceContextManager.QuiesceTidyUp;
+import org.apache.aries.jpa.container.context.transaction.impl.DestroyCallback;
 import org.apache.aries.jpa.container.context.transaction.impl.JTAEntityManager;
 import org.apache.aries.jpa.container.context.transaction.impl.JTAPersistenceContextRegistry;
 import org.osgi.framework.ServiceReference;
@@ -41,8 +45,10 @@ import org.slf4j.LoggerFactory;
  * This is registered in the Service registry to be looked up by blueprint.
  * The EntityManagerFactory interface is used to ensure a shared class space
  * with the client. Only the createEntityManager() method is supported.
+ * 
+ * Also this class receives a callback on cleanup
  */
-public class ManagedPersistenceContextFactory implements EntityManagerFactory {
+public class ManagedPersistenceContextFactory implements EntityManagerFactory, DestroyCallback {
   /** Logger */
   private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container.context");
   
@@ -50,10 +56,15 @@ public class ManagedPersistenceContextFa
   private final Map<String, Object> properties;
   private final JTAPersistenceContextRegistry registry;
   private final PersistenceContextType type;
-    
-  public ManagedPersistenceContextFactory(ServiceReference unit,
+  private final AtomicBoolean quiesce = new AtomicBoolean(false);
+  private final AtomicLong activeCount = new AtomicLong(0);
+  private final String unitName;
+  
+  private final AtomicReference<QuiesceTidyUp> tidyUp = new AtomicReference<QuiesceTidyUp>();
+  
+  public ManagedPersistenceContextFactory(String name, ServiceReference unit,
       Map<String, Object> props, JTAPersistenceContextRegistry contextRegistry) {
-
+      unitName = name;
       emf = unit;
       //Take a copy of the Map so that we don't modify the original
       properties = new HashMap<String, Object>(props);
@@ -70,7 +81,7 @@ public class ManagedPersistenceContextFa
     EntityManagerFactory factory = (EntityManagerFactory) emf.getBundle().getBundleContext().getService(emf);
     
     if(type == PersistenceContextType.TRANSACTION || type == null)
-      return new JTAEntityManager(factory, properties, registry);
+      return new JTAEntityManager(factory, properties, registry, activeCount, this);
     else {
       _logger.error("There is currently no support for extended scope EntityManagers");
       return null;
@@ -110,4 +121,25 @@ public class ManagedPersistenceContextFa
     throw new UnsupportedOperationException();
   }
 
+  /**
+   * Register an async Quiesce operation with this peristence context
+   * @param tidyUp
+   */
+  public void quiesce(QuiesceTidyUp tidyUp) {
+    this.tidyUp.set(tidyUp);
+    quiesce.set(true);
+    if(activeCount.get() == 0) {
+      tidyUp.unitQuiesced(unitName);
+    }
+  }
+
+  /**
+   * Quiesce this unit after the last context is destroyed
+   */
+  public void callback() {
+    if(quiesce.get() && 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/PersistenceContextManager.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManager.java?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManager.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManager.java Thu Aug  5 11:13:55 2010
@@ -18,17 +18,23 @@
  */
 package org.apache.aries.jpa.container.context.impl;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Map.Entry;
 
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.PersistenceContextType;
 
 import org.apache.aries.jpa.container.PersistenceUnitConstants;
 import org.apache.aries.jpa.container.context.PersistenceContextProvider;
+import org.apache.aries.jpa.container.context.transaction.impl.DestroyCallback;
 import org.apache.aries.jpa.container.context.transaction.impl.JTAPersistenceContextRegistry;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -268,7 +274,7 @@ public class PersistenceContextManager e
         }
 
         //Create the service factory
-        entityManagerServiceFactory = new ManagedPersistenceContextFactory(unit, props, persistenceContextRegistry);
+        entityManagerServiceFactory = new ManagedPersistenceContextFactory(name, unit, props, persistenceContextRegistry);
       }
      
       //Always register from outside a synchronized 
@@ -397,4 +403,117 @@ public class PersistenceContextManager e
   public void open() {
     super.open(true);
   }
+
+  /**
+   * Call this method to quiesce a given bundle
+   * @param bundleToQuiesce The bundle to quiesce
+   * @param callback A callback indicating that we have finished quiescing
+   */
+  public void quiesceUnits(Bundle bundleToQuiesce, DestroyCallback callback) {
+    
+    //Find the units supplied by this bundle
+    List<String> units = new ArrayList<String>();
+    synchronized (this) {
+      Iterator<Entry<String, ServiceReference>> it = persistenceUnits.entrySet().iterator();
+      while(it.hasNext()) {
+        Entry<String, ServiceReference> entry = it.next();
+        ServiceReference value = entry.getValue();
+        //If the quiescing bundle supplies the unit then unget the unit and remove it
+        if(value.getBundle().equals(bundleToQuiesce)) {
+          units.add(entry.getKey());
+          context.ungetService(entry.getValue());
+          //remove it to prevent other units that start using it
+          it.remove();
+        }
+      }
+    }
+    //If there are no units then our work is done!
+    if(units.isEmpty()) {
+      callback.callback();
+    } else {
+      //Find the ManagedFactories for the persistence unit
+      List<ManagedPersistenceContextFactory> factoriesToQuiesce = new ArrayList<ManagedPersistenceContextFactory>();
+      synchronized (this) {
+        for(String name : units) {
+          ServiceRegistration reg = entityManagerRegistrations.get(name);
+          if(reg != null) {
+            ManagedPersistenceContextFactory fact = (ManagedPersistenceContextFactory) bundleToQuiesce.getBundleContext().getService(reg.getReference());
+            if(fact != null)
+              factoriesToQuiesce.add(fact);
+          }
+        }
+      }
+      //If no factories are registered then we're done
+      if(factoriesToQuiesce.isEmpty()) {
+        callback.callback();
+      } else { 
+        //Create a new Tidy up helper and tell the factories to tidy up
+        QuiesceTidyUp tidyUp = new QuiesceTidyUp(units, callback);
+        
+        for(ManagedPersistenceContextFactory fact : factoriesToQuiesce) {
+          fact.quiesce(tidyUp);
+        }
+      }
+    }
+  }
+  
+  /**
+   * Quiesce all the persistence units managed by this {@link PersistenceContextManager}
+   * @param callback
+   */
+  public void quiesceAllUnits(final DestroyCallback callback) {
+    
+    Collection<String> names = new ArrayList<String>();
+    Collection<ServiceRegistration> factoriesToQuiesce = new ArrayList<ServiceRegistration>();
+   
+    //Get all the resources
+    synchronized (this) {
+      names.addAll(entityManagerRegistrations.keySet());
+      factoriesToQuiesce.addAll(entityManagerRegistrations.values());
+    }
+    //If there are no names or factories then we're done
+    if(names.isEmpty() || factoriesToQuiesce.isEmpty()) {
+      callback.callback();
+    } else {
+      
+      //Register an async tidy up
+      QuiesceTidyUp tidyUp = new QuiesceTidyUp(names, callback);
+      
+      for(ServiceRegistration reg : factoriesToQuiesce) {
+        ManagedPersistenceContextFactory fact = (ManagedPersistenceContextFactory) reg.getReference().getBundle().getBundleContext().getService(reg.getReference());
+        fact.quiesce(tidyUp);
+      }
+    }
+  }
+  
+  /**
+   * An asynchronous tidy up operation
+   */
+  class QuiesceTidyUp {
+    //The units being tidied up
+    private final Set<String> units;
+    //The callback for when we're done
+    private final DestroyCallback dc;
+    
+    public QuiesceTidyUp(Collection<String> units,
+        DestroyCallback callback) {
+      this.units = new HashSet<String>(units);
+      dc = callback;
+    }
+    
+    public void unitQuiesced(String name) {
+      unregisterEM(name);
+      boolean winner;
+      synchronized(this) {
+        winner = units.remove(name) && units.isEmpty();
+      }
+      
+      if(winner) {
+        dc.callback();
+      }
+    }
+  }
+
+
+  
 }

Added: 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=982543&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/QuiesceParticipantImpl.java (added)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/QuiesceParticipantImpl.java Thu Aug  5 11:13:55 2010
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.jpa.container.context.impl;
+
+import java.util.List;
+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.jpa.container.context.transaction.impl.DestroyCallback;
+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 contexts. 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 GlobalPersistenceManager instance */
+		private final GlobalPersistenceManager mgr; 
+
+		public QuiesceBundle(QuiesceCallback callback, Bundle bundleToQuiesce,
+				GlobalPersistenceManager 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-Context-ThreadPool");
+      t.setDaemon(true);
+      return t;
+    }
+  });
+	
+	/** The Global manager for persistence contexts */
+	private final GlobalPersistenceManager mgr;
+	
+	
+	public QuiesceParticipantImpl(GlobalPersistenceManager 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) {
+		    
+		  }
+		  //If we are quiescing, then we need to quiesce this threadpool!
+		  if(b.equals(mgr.getBundle()))
+		    executor.shutdown();
+		}
+	}
+  
+  /**
+   * Close down this object
+   */
+  public void callback() {
+    executor.shutdown();
+    try {
+      executor.awaitTermination(10, TimeUnit.SECONDS);
+    } catch (InterruptedException e) {
+      //We don't care
+    }
+    executor.shutdownNow();
+  }
+}

Added: incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/DestroyCallback.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/DestroyCallback.java?rev=982543&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/DestroyCallback.java (added)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/DestroyCallback.java Thu Aug  5 11:13:55 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 WARRANTIES OR 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.context.transaction.impl;
+
+/**
+ * A callback to indicate that a destroy operation has completed
+ */
+public interface DestroyCallback {
+  public void callback();
+}

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java Thu Aug  5 11:13:55 2010
@@ -19,6 +19,7 @@
 package org.apache.aries.jpa.container.context.transaction.impl;
 
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
@@ -48,6 +49,12 @@ public class JTAEntityManager implements
   private final Map<String, Object> props;
   /** A registry for creating new persistence contexts */
   private final JTAPersistenceContextRegistry reg;
+  /** The number of EntityManager instances that are open */
+  private final AtomicLong instanceCount;
+  /** A callback for when we're quiescing */
+  private final DestroyCallback callback;
+  
+  
   /** 
    * The entity manager to use when there is no transaction. Note that there is one of these
    * per injection site.
@@ -55,10 +62,13 @@ public class JTAEntityManager implements
   private EntityManager detachedManager = null;
   
   public JTAEntityManager(EntityManagerFactory factory,
-      Map<String, Object> properties, JTAPersistenceContextRegistry registry) {
+      Map<String, Object> properties, JTAPersistenceContextRegistry registry, AtomicLong activeCount,
+      DestroyCallback onDestroy) {
     emf = factory;
     props = properties;
     reg = registry;
+    instanceCount = activeCount;
+    callback = onDestroy;
   }
 
   /**
@@ -70,10 +80,10 @@ public class JTAEntityManager implements
   private EntityManager getPersistenceContext(boolean forceTransaction) 
   {
     if (forceTransaction) {
-      return reg.getCurrentPersistenceContext(emf, props);
+      return reg.getCurrentPersistenceContext(emf, props, instanceCount, callback);
     } else {
       if (reg.isTransactionActive()) {
-        return reg.getCurrentPersistenceContext(emf, props);
+        return reg.getCurrentPersistenceContext(emf, props, instanceCount, callback);
       } else {
         if(!!!reg.jtaIntegrationAvailable() && _logger.isDebugEnabled())
           _logger.debug("No integration with JTA transactions is available. No transaction context is active.");

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java Thu Aug  5 11:13:55 2010
@@ -20,7 +20,8 @@ package org.apache.aries.jpa.container.c
 
 import java.util.IdentityHashMap;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
@@ -28,14 +29,16 @@ import javax.persistence.TransactionRequ
 import javax.transaction.Synchronization;
 import javax.transaction.TransactionSynchronizationRegistry;
 
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * This class is used to manage the lifecycle of JTA peristence contexts
  */
-public final class JTAPersistenceContextRegistry {
+public final class JTAPersistenceContextRegistry extends ServiceTracker {
   /** Logger */
   private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container.context");
   /** The unique key we use to find our Map */
@@ -62,13 +65,15 @@ public final class JTAPersistenceContext
    * The transaction synchronization registry, used to determine the currently
    * active transaction, and to register for post-commit cleanup. 
    */
-  private TransactionSynchronizationRegistry tranRegistry;
+  private final AtomicReference<TransactionSynchronizationRegistry> tranRegistry = new AtomicReference<TransactionSynchronizationRegistry>();
   
-  /** 
-   * A flag to indicate whether the {@link TransactionSynchronizationRegistry} is available. 
-   * The initial value is false, as defined by {@link AtomicBoolean#AtomicBoolean()}.
-   */
-  private final AtomicBoolean registryAvailable = new AtomicBoolean();
+  /** The reference for our TSR service */
+  private AtomicReference<ServiceReference> tranRegistryRef = new AtomicReference<ServiceReference>();
+
+  public JTAPersistenceContextRegistry(BundleContext context) {
+    super(context, TransactionSynchronizationRegistry.class.getName(), null);
+    open();
+  }
 
   /**
    * Get a PersistenceContext for the current transaction. The persistence context will 
@@ -77,13 +82,17 @@ public final class JTAPersistenceContext
    * @param persistenceUnit The peristence unit to create the persitence context from
    * @param properties  Any properties that should be passed on the call to {@code createEntityManager()}. 
    * The properties are NOT used for retrieving an already created persistence context.
+   * @param activeCount The AtomicLong for counting instances
+   * @param cbk A callback called when the instance is destroyed
    * 
    * @return A persistence context associated with the current transaction. Note that this will
    *         need to be wrappered to obey the JPA spec by throwing the correct exceptions
    * @throws {@link TransactionRequiredException} if there is no active transaction.
    */
   @SuppressWarnings("unchecked")
-  public final EntityManager getCurrentPersistenceContext(EntityManagerFactory persistenceUnit, Map<?,?> properties) throws TransactionRequiredException
+  public final EntityManager getCurrentPersistenceContext(
+      EntityManagerFactory persistenceUnit, Map<?,?> properties, AtomicLong activeCount,
+      DestroyCallback cbk) throws TransactionRequiredException
   {
     //There will only ever be one thread associated with a transaction at a given time
     //As a result, it is only the outer map that needs to be thread safe.
@@ -98,11 +107,11 @@ public final class JTAPersistenceContext
       }
     }
     EntityManager toReturn = null;
-    
+    TransactionSynchronizationRegistry tsr = tranRegistry.get();
     //Get hold of the Map. If there is no Map already registered then add one.
     //We don't need to worry about a race condition, as no other thread will
     //share our transaction and be able to access our Map
-    Map<EntityManagerFactory, EntityManager> contextsForTransaction = (Map<EntityManagerFactory, EntityManager>) tranRegistry.getResource(EMF_MAP_KEY);
+    Map<EntityManagerFactory, EntityManager> contextsForTransaction = (Map<EntityManagerFactory, EntityManager>) tsr.getResource(EMF_MAP_KEY);
     
     //If we have a map then find an EntityManager, else create a new Map add it to the registry, and register for cleanup
     if(contextsForTransaction != null) {
@@ -110,10 +119,10 @@ public final class JTAPersistenceContext
     } else {
       contextsForTransaction = new IdentityHashMap<EntityManagerFactory, EntityManager>();
       try {
-        tranRegistry.putResource(EMF_MAP_KEY, contextsForTransaction);
+        tsr.putResource(EMF_MAP_KEY, contextsForTransaction);
       } catch (IllegalStateException e) {
-        _logger.warn("Unable to create a persistence context for the transaction {} because the is not active", new Object[] {tranRegistry.getTransactionKey()});
-        throw new TransactionRequiredException("Unable to assiociate resources with transaction " + tranRegistry.getTransactionKey());
+        _logger.warn("Unable to create a persistence context for the transaction {} because the is not active", new Object[] {tsr.getTransactionKey()});
+        throw new TransactionRequiredException("Unable to assiociate resources with transaction " + tsr.getTransactionKey());
       }
     }
     
@@ -121,18 +130,19 @@ public final class JTAPersistenceContext
     if(toReturn == null) {
       toReturn = (properties == null) ? persistenceUnit.createEntityManager() : persistenceUnit.createEntityManager(properties);
       if(_logger.isDebugEnabled())
-        _logger.debug("Created a new persistence context {} for transaction {}.", new Object[] {toReturn, tranRegistry.getTransactionKey()});
+        _logger.debug("Created a new persistence context {} for transaction {}.", new Object[] {toReturn, tsr.getTransactionKey()});
       try {
-        tranRegistry.registerInterposedSynchronization(new EntityManagerClearUp(toReturn));
+        tsr.registerInterposedSynchronization(new EntityManagerClearUp(toReturn, activeCount, cbk));
       } catch (IllegalStateException e) {
-        _logger.warn("No persistence context could be created as the JPA container could not register a synchronization with the transaction {}.", new Object[] {tranRegistry.getTransactionKey()});
+        _logger.warn("No persistence context could be created as the JPA container could not register a synchronization with the transaction {}.", new Object[] {tsr.getTransactionKey()});
         toReturn.close();
-        throw new TransactionRequiredException("Unable to synchronize with transaction " + tranRegistry.getTransactionKey());
+        throw new TransactionRequiredException("Unable to synchronize with transaction " + tsr.getTransactionKey());
       }
       contextsForTransaction.put(persistenceUnit, toReturn);
+      activeCount.incrementAndGet();
     } else {
       if(_logger.isDebugEnabled())
-        _logger.debug("Re-using a persistence context for transaction " + tranRegistry.getTransactionKey());
+        _logger.debug("Re-using a persistence context for transaction " + tsr.getTransactionKey());
     }
     return toReturn;
   }
@@ -143,15 +153,8 @@ public final class JTAPersistenceContext
    */
   public final boolean isTransactionActive()
   {
-    return registryAvailable.get() && tranRegistry.getTransactionKey() != null;
-  }
-  
-  /**
-   * Provide a {@link TransactionSynchronizationRegistry} to use
-   * @param tranRegistry
-   */
-  public final void setTranRegistry(TransactionSynchronizationRegistry tranRegistry) {
-    this.tranRegistry = tranRegistry;
+    TransactionSynchronizationRegistry tsr = tranRegistry.get();
+    return tsr != null && tsr.getTransactionKey() != null;
   }
 
   /**
@@ -161,34 +164,93 @@ public final class JTAPersistenceContext
    */
   public final boolean jtaIntegrationAvailable()
   {
-    return registryAvailable.get();
+    return tranRegistry.get() != null;
   }
   
   /**
-   * Called by the blueprint container to indicate that a new {@link TransactionSynchronizationRegistry}
-   * will be used by the runtime
+   * Called by service tracker to indicate that a new {@link TransactionSynchronizationRegistry}
+   * is available in the runtime
    * @param ref
    */
-  public final void addRegistry(ServiceReference ref) {
-    boolean oldValue = registryAvailable.getAndSet(true);
-    if(oldValue) {
-      _logger.warn("The TransactionSynchronizationRegistry used to manage persistence contexts has been replaced." +
-      		" The new TransactionSynchronizationRegistry, {}, will now be used to manage persistence contexts." +
-      		" Managed persistence contexts may not work correctly unless the runtime uses the new JTA Transaction services implementation" +
-      		" to manage transactions.", new Object[] {ref});
-    } else {
-        _logger.info("A TransactionSynchronizationRegistry service is now available in the runtime. Managed persistence contexts will now" +
-        		"integrate with JTA transactions using {}.", new Object[] {ref});
+  @Override
+  public final Object addingService(ServiceReference ref) {
+    
+    if(tranRegistryRef.compareAndSet(null, ref)) 
+    {
+      TransactionSynchronizationRegistry tsr = (TransactionSynchronizationRegistry) context.getService(ref);
+      if(tsr != null) {
+        if(tranRegistry.compareAndSet(null, tsr)) {
+          _logger.info("A TransactionSynchronizationRegistry service is now available in the runtime. Managed persistence contexts will now" +
+              "integrate with JTA transactions using {}.", new Object[] {ref});
+        }
+        else
+        {
+          tranRegistry.set(tsr);
+          _logger.warn("The TransactionSynchronizationRegistry used to manage persistence contexts has been replaced." +
+              " The new TransactionSynchronizationRegistry, {}, will now be used to manage persistence contexts." +
+              " Managed persistence contexts may not work correctly unless the runtime uses the new JTA Transaction services implementation" +
+              " to manage transactions.", new Object[] {ref});
+        }
+      } else {
+        tranRegistryRef.compareAndSet(ref, null);
+      }
     }
+    return ref;
   }
   
-  public final void removeRegistry(ServiceReference ref) {
-    registryAvailable.set(false);
-    _logger.warn("The TransactionSynchronizationRegistry used to manage persistence contexts is no longer available." +
-        " Managed persistence contexts will no longer be able to integrate with JTA transactions, and will behave as if" +
-        " no there is no transaction context at all times until a new TransactionSynchronizationRegistry is available." +
-        " Applications using managed persistence contexts may not work correctly until a new JTA Transaction services" +
-        " implementation is available.");
+  @Override
+  public final void removedService(ServiceReference reference, Object o) {
+    if(tranRegistryRef.get() == reference) { 
+      //Our reference is going away, find a new one
+      ServiceReference[] refs = getServiceReferences();
+      ServiceReference chosenRef = null;
+      TransactionSynchronizationRegistry replacement = null;
+      if(refs != null) {
+        for(ServiceReference ref : refs) {
+          if(ref != reference) {
+            replacement = (TransactionSynchronizationRegistry) context.getService(ref);
+            if(replacement != null) {
+              chosenRef = ref;
+              break;
+            }
+          }
+        }
+      }
+      
+      if(replacement == null) {
+        TransactionSynchronizationRegistry old = tranRegistry.get();
+        tranRegistryRef.set(null);
+        tranRegistry.compareAndSet(old, null);
+        
+      _logger.warn("The TransactionSynchronizationRegistry used to manage persistence contexts is no longer available." +
+          " Managed persistence contexts will no longer be able to integrate with JTA transactions, and will behave as if" +
+          " no there is no transaction context at all times until a new TransactionSynchronizationRegistry is available." +
+          " Applications using managed persistence contexts may not work correctly until a new JTA Transaction services" +
+          " implementation is available.");
+      } else {
+        tranRegistryRef.set(chosenRef);
+        tranRegistry.set(replacement);
+      _logger.warn("The TransactionSynchronizationRegistry used to manage persistence contexts has been replaced." +
+          " The new TransactionSynchronizationRegistry, {}, will now be used to manage persistence contexts." +
+          " Managed persistence contexts may not work correctly unless the runtime uses the new JTA Transaction services implementation" +
+          " to manage transactions.", new Object[] {chosenRef});
+      }
+      context.ungetService(reference);
+      //If there was no replacement before, check again. This closes the short window if
+      //add and remove happen simultaneously
+      if(replacement == null) {
+        ServiceReference[] retryRefs = getServiceReferences();
+        if(refs != null) {
+          for(ServiceReference r : retryRefs) {
+            if(r != reference) {
+              addingService(r);
+              if(tranRegistryRef.get() != null)
+                break;
+            }
+          }
+        }
+      }
+    }
   }
   
   /**
@@ -199,13 +261,19 @@ public final class JTAPersistenceContext
 
     private final EntityManager context;
     
+    private final AtomicLong activeCount;
+    
+    private final DestroyCallback callback;
+    
     /**
      * Create a Synchronization to clear up our EntityManagers
      * @param em
      */
-    public EntityManagerClearUp(EntityManager em)
+    public EntityManagerClearUp(EntityManager em, AtomicLong instanceCount, DestroyCallback cbk)
     {
       context = em;
+      activeCount = instanceCount;
+      callback = cbk;
     }
     
     public final void beforeCompletion() {
@@ -217,10 +285,16 @@ public final class JTAPersistenceContext
       if(_logger.isDebugEnabled())
         _logger.debug("Clearing up EntityManager {} as the transaction has completed.", new Object[] {context});
       try {
-        context.close();
-      } catch (Exception e) {
-        _logger.warn("There was an error when the container closed an EntityManager", context);
+        activeCount.decrementAndGet();
+        callback.callback();
+      } finally {
+			  try{
+			    context.close();
+        } catch (Exception e) {
+          _logger.warn("There was an error when the container closed an EntityManager", context);
+        }
       }
     }
   }
+
 }

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManagerTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManagerTest.java?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManagerTest.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/GlobalPersistenceManagerTest.java Thu Aug  5 11:13:55 2010
@@ -45,7 +45,7 @@ public class GlobalPersistenceManagerTes
     private Bundle framework;
     
     @Before
-    public void setup()
+    public void setup() throws Exception
     {
         framework = Skeleton.newMock(new BundleMock("framework", new Hashtable<Object, Object>()), Bundle.class);
 
@@ -59,8 +59,7 @@ public class GlobalPersistenceManagerTes
         Skeleton.getSkeleton(otherClient).setReturnValue(new MethodCall(Bundle.class, "getBundleContext"), ctx);        
         
         sut = new GlobalPersistenceManager();
-        sut.setRegistry(new JTAPersistenceContextRegistry());
-        
+        sut.start(ctx);
         Skeleton.getSkeleton(framework.getBundleContext()).setReturnValue(
                 new MethodCall(BundleContext.class, "getBundles"), new Bundle[] {framework, client, otherClient});
     }

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java Thu Aug  5 11:13:55 2010
@@ -26,11 +26,16 @@ import java.util.HashMap;
 import java.util.Hashtable;
 
 import javax.persistence.EntityManagerFactory;
+import javax.transaction.TransactionSynchronizationRegistry;
 
 import org.apache.aries.jpa.container.PersistenceUnitConstants;
 import org.apache.aries.jpa.container.context.PersistenceContextProvider;
+import org.apache.aries.jpa.container.context.transaction.impl.DestroyCallback;
+import org.apache.aries.jpa.container.context.transaction.impl.JTAPersistenceContextRegistry;
+import org.apache.aries.jpa.container.context.transaction.impl.TranSyncRegistryMock;
 import org.apache.aries.mocks.BundleContextMock;
 import org.apache.aries.mocks.BundleMock;
+import org.apache.aries.unittest.mocks.MethodCall;
 import org.apache.aries.unittest.mocks.Skeleton;
 import org.junit.After;
 import org.junit.Before;
@@ -65,7 +70,7 @@ public class PersistenceContextManagerTe
     emf1 = Skeleton.newMock(EntityManagerFactory.class);
     emf2 = Skeleton.newMock(EntityManagerFactory.class);
     context = Skeleton.newMock(new BundleMock("system.bundle", new Hashtable<Object, Object>()), Bundle.class).getBundleContext();
-    mgr = new PersistenceContextManager(context, null);
+    mgr = new PersistenceContextManager(context, new JTAPersistenceContextRegistry(context));
     mgr.open();
   }
   
@@ -92,7 +97,7 @@ public class PersistenceContextManagerTe
     
     mgr.registerContext(unitName, client1, new HashMap<String, Object>());
     
-    assertContextRegistered(unitName);
+    assertUniqueContextRegistered(unitName);
   }
 
   /**
@@ -142,7 +147,7 @@ public class PersistenceContextManagerTe
     
     reg1 = registerUnit(emf1, unitName, TRUE);
     
-    assertContextRegistered(unitName);
+    assertUniqueContextRegistered(unitName);
   }
 
   /**
@@ -203,11 +208,11 @@ public class PersistenceContextManagerTe
     
     mgr.registerContext("unit", client1, new HashMap<String, Object>());
     
-    assertContextRegistered("unit");
+    assertUniqueContextRegistered("unit");
     
     mgr.unregisterContext("context", client1);
     
-    assertContextRegistered("unit");
+    assertUniqueContextRegistered("unit");
   }
   
   /**
@@ -220,9 +225,9 @@ public class PersistenceContextManagerTe
   {
     testAddDifferentContext();
     reg2 = registerUnit(emf2, "context", TRUE);
-    assertContextRegistered("context");
+    assertUniqueContextRegistered("context");
     reg1.unregister();
-    assertContextRegistered("context");
+    assertUniqueContextRegistered("context");
     reg2.unregister();
     assertNoContextRegistered();
   }
@@ -237,15 +242,272 @@ public class PersistenceContextManagerTe
     testContextThenUnit();
     
     mgr.registerContext("unit", client2, new HashMap<String, Object>());
-    assertContextRegistered("unit");
+    assertUniqueContextRegistered("unit");
     
     mgr.unregisterContext("unit", client1);
-    assertContextRegistered("unit");
+    assertUniqueContextRegistered("unit");
     
     mgr.unregisterContext("unit", client2);
     assertNoContextRegistered();
   }
   
+  /**
+   * Test that we can quiesce an unused context
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testNoOpQuiesce() throws InvalidSyntaxException
+  {
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceUnits(b, cbk);
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we can quiesce an unused context
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testBasicQuiesce() throws InvalidSyntaxException
+  {
+    testContextThenUnit();
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceUnits(b, cbk);
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we can quiesce multiple contexts
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testMultipleContextQuiesce() throws InvalidSyntaxException
+  {
+    assertNoContextRegistered();
+    
+    reg1 = registerUnit(emf1, "unit", TRUE);
+    mgr.registerContext("unit", client1, new HashMap<String, Object>());
+    
+    reg2 = registerUnit(emf1, "unit2", TRUE);
+    mgr.registerContext("unit2", client2, new HashMap<String, Object>());
+    
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceUnits(b, cbk);
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we can quiesce multiple contexts
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testActiveContextsQuiesce() throws InvalidSyntaxException
+  {
+    TranSyncRegistryMock backingTSR = new TranSyncRegistryMock();
+    TransactionSynchronizationRegistry tsr = Skeleton.newMock(backingTSR, TransactionSynchronizationRegistry.class);
+    
+    context.registerService(TransactionSynchronizationRegistry.class.getName(), tsr, new Hashtable<String, Object>());
+    
+    assertNoContextRegistered();
+    
+    reg1 = registerUnit(emf1, "unit", TRUE);
+    mgr.registerContext("unit", client1, new HashMap<String, Object>());
+    
+    reg2 = registerUnit(emf2, "unit2", TRUE);
+    mgr.registerContext("unit2", client2, new HashMap<String, Object>());
+    
+    ServiceReference[] refs = context.getServiceReferences(EntityManagerFactory.class.getName(),"(&(" + 
+        PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE + "=true)(osgi.unit.name=unit))");
+    
+    assertEquals("Wrong number of services found", 1, refs.length);
+    
+    ServiceReference[] refs2 = context.getServiceReferences(EntityManagerFactory.class.getName(),"(&(" + 
+        PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE + "=true)(osgi.unit.name=unit2))");
+    
+    assertEquals("Wrong number of services found", 1, refs2.length);
+    
+    EntityManagerFactory emf = (EntityManagerFactory) context.getService(refs[0]);
+    
+    EntityManagerFactory emf2 = (EntityManagerFactory) context.getService(refs2[0]);
+    
+    backingTSR.setTransactionKey("new");
+    
+    emf.createEntityManager().persist(new Object());
+    emf2.createEntityManager().persist(new Object());
+    
+    
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceUnits(b, cbk);
+    
+    Skeleton.getSkeleton(cbk).assertNotCalled(new MethodCall(DestroyCallback.class,
+        "callback"));
+    
+    backingTSR.setTransactionKey("new2");
+    
+    emf2.createEntityManager().persist(new Object());
+    
+    Skeleton.getSkeleton(cbk).assertNotCalled(new MethodCall(DestroyCallback.class,
+        "callback"));
+    
+    backingTSR.afterCompletion("new");
+    
+    Skeleton.getSkeleton(cbk).assertNotCalled(new MethodCall(DestroyCallback.class,
+        "callback"));
+    
+    assertUniqueContextRegistered("unit2");
+    
+    backingTSR.afterCompletion("new2");
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we can quiesce an unused context
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testNoOpQuiesceAll() throws InvalidSyntaxException
+  {
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceAllUnits(cbk);
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we can quiesce an unused context
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testBasicQuiesceAll() throws InvalidSyntaxException
+  {
+    testContextThenUnit();
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceAllUnits(cbk);
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we can quiesce multiple contexts
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testMultipleContextQuiesceAll() throws InvalidSyntaxException
+  {
+    assertNoContextRegistered();
+    
+    reg1 = registerUnit(emf1, "unit", TRUE);
+    mgr.registerContext("unit", client1, new HashMap<String, Object>());
+    
+    reg2 = registerUnit(emf1, "unit2", TRUE);
+    mgr.registerContext("unit2", client2, new HashMap<String, Object>());
+    
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceAllUnits(cbk);
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we can quiesce multiple contexts
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testActiveContextsQuiesceAll() throws InvalidSyntaxException
+  {
+    TranSyncRegistryMock backingTSR = new TranSyncRegistryMock();
+    TransactionSynchronizationRegistry tsr = Skeleton.newMock(backingTSR, TransactionSynchronizationRegistry.class);
+    
+    context.registerService(TransactionSynchronizationRegistry.class.getName(), tsr, new Hashtable<String, Object>());
+    
+    assertNoContextRegistered();
+    
+    reg1 = registerUnit(emf1, "unit", TRUE);
+    mgr.registerContext("unit", client1, new HashMap<String, Object>());
+    
+    reg2 = registerUnit(emf2, "unit2", TRUE);
+    mgr.registerContext("unit2", client2, new HashMap<String, Object>());
+    
+    ServiceReference[] refs = context.getServiceReferences(EntityManagerFactory.class.getName(),"(&(" + 
+        PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE + "=true)(osgi.unit.name=unit))");
+    
+    assertEquals("Wrong number of services found", 1, refs.length);
+    
+    ServiceReference[] refs2 = context.getServiceReferences(EntityManagerFactory.class.getName(),"(&(" + 
+        PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE + "=true)(osgi.unit.name=unit2))");
+    
+    assertEquals("Wrong number of services found", 1, refs2.length);
+    
+    EntityManagerFactory emf = (EntityManagerFactory) context.getService(refs[0]);
+    
+    EntityManagerFactory emf2 = (EntityManagerFactory) context.getService(refs2[0]);
+    
+    backingTSR.setTransactionKey("new");
+    
+    emf.createEntityManager().persist(new Object());
+    emf2.createEntityManager().persist(new Object());
+    
+    
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    Bundle b = context.getBundle();
+    mgr.quiesceAllUnits(cbk);
+    
+    Skeleton.getSkeleton(cbk).assertNotCalled(new MethodCall(DestroyCallback.class,
+        "callback"));
+    
+    backingTSR.setTransactionKey("new2");
+    
+    emf2.createEntityManager().persist(new Object());
+    
+    Skeleton.getSkeleton(cbk).assertNotCalled(new MethodCall(DestroyCallback.class,
+        "callback"));
+    
+    backingTSR.afterCompletion("new");
+    
+    Skeleton.getSkeleton(cbk).assertNotCalled(new MethodCall(DestroyCallback.class,
+        "callback"));
+    
+    assertUniqueContextRegistered("unit2");
+    
+    backingTSR.afterCompletion("new2");
+    
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 1);
+    
+    assertNoContextRegistered();
+  }
   
   private ServiceRegistration registerUnit(EntityManagerFactory emf, String name, Boolean managed) {
     
@@ -270,7 +532,7 @@ public class PersistenceContextManagerTe
     assertNull(refs);
   }
   
-  private void assertContextRegistered(String name) throws InvalidSyntaxException {
+  private void assertUniqueContextRegistered(String name) throws InvalidSyntaxException {
     BundleContextMock.assertServiceExists(EntityManagerFactory.class.getName());
     
     ServiceReference[] refs = context.getServiceReferences(EntityManagerFactory.class.getName(), "("+PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE+"=*)");

Modified: incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java (original)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java Thu Aug  5 11:13:55 2010
@@ -18,83 +18,30 @@
  */
 package org.apache.aries.jpa.container.context.transaction.impl;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.TransactionRequiredException;
-import javax.transaction.Status;
-import javax.transaction.Synchronization;
 import javax.transaction.TransactionSynchronizationRegistry;
 
 import org.apache.aries.unittest.mocks.MethodCall;
 import org.apache.aries.unittest.mocks.Skeleton;
 import org.junit.Before;
 import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 
 public class JTAPersistenceContextRegistryTest {
 
-  private static class TranSyncRegistryMock
-  {
-    private String key;
-    
-    private Map<String, List<Synchronization>> syncs = new HashMap<String, List<Synchronization>>();
-    
-    private Map<String, Map<Object,Object>> resources = new HashMap<String, Map<Object,Object>>();
-    
-    public void setTransactionKey(String s)
-    {
-      key = s;
-    }
-    
-    public Object getTransactionKey() {
-      return key;
-    }
-
-    public void registerInterposedSynchronization(Synchronization arg0) {
-      List<Synchronization> list = syncs.get(key);
-      if(list == null) {
-        list = new ArrayList<Synchronization>();
-        syncs.put(key, list);
-      }
-       list.add(arg0);
-    }
-    
-    public Object getResource(Object o) {
-      Object toReturn = null;
-      Map<Object, Object> map = resources.get(key);
-      if(map != null)
-        toReturn = map.get(o);
-      return toReturn;
-    }
-    
-    public void putResource(Object resourceKey, Object value) {
-      Map<Object, Object> map = resources.get(key);
-      if(map == null) {
-        map = new HashMap<Object, Object>();
-        resources.put(key, map);
-      }
-      map.put(resourceKey, value);
-    }
-    
-    
-    public void afterCompletion(String s)
-    {
-      for(Synchronization sync : syncs.get(s))
-        sync.afterCompletion(Status.STATUS_COMMITTED);
-      
-      resources.remove(s);
-    }
-  }
-  
   private TranSyncRegistryMock reg;
   
   private EntityManagerFactory emf1;
@@ -103,11 +50,17 @@ public class JTAPersistenceContextRegist
   private Map<Object,Object> props2;
   
   private JTAPersistenceContextRegistry contexts;
+  private BundleContext ctx;
+  private TransactionSynchronizationRegistry tsr;
+
+  private ServiceReference ref;
   
   @Before
   public void setup() 
   {
     reg = new TranSyncRegistryMock();
+    tsr = Skeleton.newMock(reg, TransactionSynchronizationRegistry.class);
+    ctx = Skeleton.newMock(BundleContext.class);
 
     props1 = new HashMap<Object, Object>();
     props1.put("prop1", "value1");
@@ -130,9 +83,11 @@ public class JTAPersistenceContextRegist
         "createEntityManager", props2), Skeleton.newMock(EntityManager.class));
 
     
-    contexts = new JTAPersistenceContextRegistry();
-    contexts.setTranRegistry(Skeleton.newMock(reg, TransactionSynchronizationRegistry.class));
-    contexts.addRegistry(null);
+    contexts = new JTAPersistenceContextRegistry(ctx);
+    ref = Skeleton.newMock(ServiceReference.class);
+    Skeleton.getSkeleton(ctx).setReturnValue(new MethodCall(BundleContext.class,
+        "getService", ref), tsr);
+    contexts.addingService(ref);
   }
   
   @Test
@@ -150,50 +105,65 @@ public class JTAPersistenceContextRegist
   @Test
   public void testMultiGetsOneTran()
   {
+    AtomicLong useCount = new AtomicLong(0);
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
     reg.setTransactionKey("");
     
-    EntityManager em1a = contexts.getCurrentPersistenceContext(emf1, props1);
-    EntityManager em1b = contexts.getCurrentPersistenceContext(emf1, props1);
+    EntityManager em1a = contexts.getCurrentPersistenceContext(emf1, props1, useCount, cbk);
+    EntityManager em1b = contexts.getCurrentPersistenceContext(emf1, props1, useCount, cbk);
     
     Skeleton.getSkeleton(emf1).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props1), 1);
     Skeleton.getSkeleton(emf1).assertNotCalled(new MethodCall(EntityManagerFactory.class, "createEntityManager"));
     Skeleton.getSkeleton(em1a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
     assertSame("We should get the same delegate!", em1a, em1b);
+    assertEquals("Expected only one creation", 1, useCount.get());
+    Skeleton.getSkeleton(cbk).assertSkeletonNotCalled();
     
-    EntityManager em2a = contexts.getCurrentPersistenceContext(emf2, props1);
-    EntityManager em2b = contexts.getCurrentPersistenceContext(emf2, props1);
+    EntityManager em2a = contexts.getCurrentPersistenceContext(emf2, props1, useCount, cbk);
+    EntityManager em2b = contexts.getCurrentPersistenceContext(emf2, props1, useCount, cbk);
     
     Skeleton.getSkeleton(emf2).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props1), 1);
     Skeleton.getSkeleton(emf2).assertNotCalled(new MethodCall(EntityManagerFactory.class, "createEntityManager"));
     Skeleton.getSkeleton(em2a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
     assertSame("We should get the same delegate!", em2a, em2b);
+    assertEquals("Expected a second creation", 2, useCount.get());
+    Skeleton.getSkeleton(cbk).assertSkeletonNotCalled();
     
     reg.afterCompletion("");
     
     Skeleton.getSkeleton(em1a).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"),1);
     Skeleton.getSkeleton(em2a).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"),1);
+    assertEquals("Expected creations to be uncounted", 0, useCount.get());
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+        "callback"), 2);
   }
   
   @Test
   public void testMultiGetsMultiTrans()
   {
+    AtomicLong useCount = new AtomicLong(0);
+    DestroyCallback cbk = Skeleton.newMock(DestroyCallback.class);
+    
     reg.setTransactionKey("a");
-    EntityManager em1a = contexts.getCurrentPersistenceContext(emf1, props1);
+    EntityManager em1a = contexts.getCurrentPersistenceContext(emf1, props1, useCount, cbk);
     reg.setTransactionKey("b");
-    EntityManager em1b = contexts.getCurrentPersistenceContext(emf1, props2);
+    EntityManager em1b = contexts.getCurrentPersistenceContext(emf1, props2, useCount, cbk);
     
     Skeleton.getSkeleton(emf1).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props1), 1);
     Skeleton.getSkeleton(emf1).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props2), 1);
-   
+    
     assertNotSame("We should not get the same delegate!", em1a, em1b);
     
     Skeleton.getSkeleton(em1a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
     Skeleton.getSkeleton(em1b).assertNotCalled(new MethodCall(EntityManager.class, "close"));
     
+    assertEquals("Expected two creations", 2, useCount.get());
+    Skeleton.getSkeleton(cbk).assertSkeletonNotCalled();
+    
     reg.setTransactionKey("a");
-    EntityManager em2a = contexts.getCurrentPersistenceContext(emf2, props1);
+    EntityManager em2a = contexts.getCurrentPersistenceContext(emf2, props1, useCount, cbk);
     reg.setTransactionKey("b");
-    EntityManager em2b = contexts.getCurrentPersistenceContext(emf2, props2);
+    EntityManager em2b = contexts.getCurrentPersistenceContext(emf2, props2, useCount, cbk);
     
     Skeleton.getSkeleton(emf2).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props1), 1);
     Skeleton.getSkeleton(emf2).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props2), 1);
@@ -203,6 +173,9 @@ public class JTAPersistenceContextRegist
     Skeleton.getSkeleton(em2a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
     Skeleton.getSkeleton(em2b).assertNotCalled(new MethodCall(EntityManager.class, "close"));
     
+    assertEquals("Expected four creations", 4, useCount.get());
+    Skeleton.getSkeleton(cbk).assertSkeletonNotCalled();
+    
     reg.setTransactionKey("b");
     reg.afterCompletion("b");
     
@@ -211,6 +184,10 @@ public class JTAPersistenceContextRegist
     Skeleton.getSkeleton(em2a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
     Skeleton.getSkeleton(em2b).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"), 1);
     
+    assertEquals("Expected two uncreations", 2, useCount.get());
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+    "callback"), 2);
+    
     reg.setTransactionKey("a");
     reg.afterCompletion("a");
     
@@ -218,41 +195,55 @@ public class JTAPersistenceContextRegist
     Skeleton.getSkeleton(em1b).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"), 1);
     Skeleton.getSkeleton(em2a).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"), 1);
     Skeleton.getSkeleton(em2b).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"), 1);
+    assertEquals("Expected no remaining instances", 0, useCount.get());
+    Skeleton.getSkeleton(cbk).assertCalledExactNumberOfTimes(new MethodCall(DestroyCallback.class,
+    "callback"), 4);
   }
   
   @Test
   public void testNoTranSyncRegistry() {
-    JTAPersistenceContextRegistry registry = new JTAPersistenceContextRegistry();
-    //blueprint will still call our setter
-    TransactionSynchronizationRegistry tranSyncReg = Skeleton.newMock(reg, TransactionSynchronizationRegistry.class);
-    registry.setTranRegistry(tranSyncReg);
+    JTAPersistenceContextRegistry registry = new JTAPersistenceContextRegistry(ctx);
     
     reg.setTransactionKey(null);
     
     assertFalse(registry.jtaIntegrationAvailable());
     assertFalse(registry.isTransactionActive());
     
-    Skeleton.getSkeleton(tranSyncReg).assertSkeletonNotCalled();
+    Skeleton.getSkeleton(tsr).assertSkeletonNotCalled();
     
     reg.setTransactionKey("");
     
     assertFalse(registry.jtaIntegrationAvailable());
     assertFalse(registry.isTransactionActive());
     
-    Skeleton.getSkeleton(tranSyncReg).assertSkeletonNotCalled();
+    reg.setTransactionKey(null);
+    contexts.removedService(ref, ref);
+    
+    assertFalse(contexts.jtaIntegrationAvailable());
+    assertFalse(contexts.isTransactionActive());
+    
+    Skeleton.getSkeleton(tsr).assertSkeletonNotCalled();
+    
+    reg.setTransactionKey("");
+    
+    assertFalse(contexts.jtaIntegrationAvailable());
+    assertFalse(contexts.isTransactionActive());
+    
+    
+    Skeleton.getSkeleton(tsr).assertSkeletonNotCalled();
   }
   
   @Test(expected=TransactionRequiredException.class)
   public void testGetNoTran() {
     reg.setTransactionKey(null);
-    contexts.getCurrentPersistenceContext(emf1, props1);
+    contexts.getCurrentPersistenceContext(emf1, props1, new AtomicLong(), Skeleton.newMock(DestroyCallback.class));
   }
   
   @Test(expected=TransactionRequiredException.class)
   public void testGetNoTranSyncRegistry() {
     reg.setTransactionKey("");
-    contexts.removeRegistry(null);
-    contexts.getCurrentPersistenceContext(emf1, props1);
+    contexts.removedService(ref, ref);
+    contexts.getCurrentPersistenceContext(emf1, props1, new AtomicLong(), Skeleton.newMock(DestroyCallback.class));
   }
   
 }

Added: incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/TranSyncRegistryMock.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/TranSyncRegistryMock.java?rev=982543&view=auto
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/TranSyncRegistryMock.java (added)
+++ incubator/aries/trunk/jpa/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/TranSyncRegistryMock.java Thu Aug  5 11:13:55 2010
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.jpa.container.context.transaction.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+
+public class TranSyncRegistryMock
+{
+  private String key;
+  
+  private Map<String, List<Synchronization>> syncs = new HashMap<String, List<Synchronization>>();
+  
+  private Map<String, Map<Object,Object>> resources = new HashMap<String, Map<Object,Object>>();
+  
+  public void setTransactionKey(String s)
+  {
+    key = s;
+  }
+  
+  public Object getTransactionKey() {
+    return key;
+  }
+
+  public void registerInterposedSynchronization(Synchronization arg0) {
+    List<Synchronization> list = syncs.get(key);
+    if(list == null) {
+      list = new ArrayList<Synchronization>();
+      syncs.put(key, list);
+    }
+     list.add(arg0);
+  }
+  
+  public Object getResource(Object o) {
+    Object toReturn = null;
+    Map<Object, Object> map = resources.get(key);
+    if(map != null)
+      toReturn = map.get(o);
+    return toReturn;
+  }
+  
+  public void putResource(Object resourceKey, Object value) {
+    Map<Object, Object> map = resources.get(key);
+    if(map == null) {
+      map = new HashMap<Object, Object>();
+      resources.put(key, map);
+    }
+    map.put(resourceKey, value);
+  }
+  
+  
+  public void afterCompletion(String s)
+  {
+    for(Synchronization sync : syncs.get(s))
+      sync.afterCompletion(Status.STATUS_COMMITTED);
+    
+    resources.remove(s);
+  }
+}
\ No newline at end of file

Modified: incubator/aries/trunk/jpa/jpa-container-itest/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jpa/jpa-container-itest/pom.xml?rev=982543&r1=982542&r2=982543&view=diff
==============================================================================
--- incubator/aries/trunk/jpa/jpa-container-itest/pom.xml (original)
+++ incubator/aries/trunk/jpa/jpa-container-itest/pom.xml Thu Aug  5 11:13:55 2010
@@ -180,6 +180,46 @@
             <artifactId>org.apache.aries.jpa.blueprint.itest.bundle</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.aries.quiesce</groupId>
+            <artifactId>org.apache.aries.quiesce.api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.transaction</groupId>
+            <artifactId>org.apache.aries.transaction.manager</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.transaction</groupId>
+            <artifactId>org.apache.aries.transaction.testds</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jndi</groupId>
+            <artifactId>org.apache.aries.jndi.api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jndi</groupId>
+            <artifactId>org.apache.aries.jndi.core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jndi</groupId>
+            <artifactId>org.apache.aries.jndi.url</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.derby</groupId>
+            <artifactId>derby</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>



Mime
View raw message