You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by zo...@apache.org on 2011/02/27 19:08:17 UTC

svn commit: r1075099 [3/7] - in /aries/tags/jpa-0.1-incubating: ./ jpa-api/ jpa-api/src/ jpa-api/src/main/ jpa-api/src/main/java/ jpa-api/src/main/java/org/ jpa-api/src/main/java/org/apache/ jpa-api/src/main/java/org/apache/aries/ jpa-api/src/main/java...

Added: aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/ManagedPersistenceContextFactory.java Sun Feb 27 18:08:10 2011
@@ -0,0 +1,113 @@
+/*
+ * 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.context.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.persistence.Cache;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.PersistenceContextType;
+import javax.persistence.PersistenceUnitUtil;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.metamodel.Metamodel;
+
+import org.apache.aries.jpa.container.context.PersistenceContextProvider;
+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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+/**
+ * A factory that can lazily create managed persistence contexts.
+ * 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.
+ */
+public class ManagedPersistenceContextFactory implements EntityManagerFactory {
+  /** Logger */
+  private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container.context");
+  
+  private final ServiceReference emf;
+  private final Map<String, Object> properties;
+  private final JTAPersistenceContextRegistry registry;
+  private final PersistenceContextType type;
+    
+  public ManagedPersistenceContextFactory(ServiceReference unit,
+      Map<String, Object> props, JTAPersistenceContextRegistry contextRegistry) {
+
+      emf = unit;
+      //Take a copy of the Map so that we don't modify the original
+      properties = new HashMap<String, Object>(props);
+      registry = contextRegistry;
+      //Remove our internal property so that it doesn't get passed on the createEntityManager call
+      type = (PersistenceContextType) properties.remove(PersistenceContextProvider.PERSISTENCE_CONTEXT_TYPE);
+  }
+
+  public EntityManager createEntityManager() {
+    if(_logger.isDebugEnabled()) {
+      _logger.debug("Creating a container managed entity manager for the perstence unit {} with the following properties {}",
+          new Object[] {emf, properties});
+    }
+    EntityManagerFactory factory = (EntityManagerFactory) emf.getBundle().getBundleContext().getService(emf);
+    
+    if(type == PersistenceContextType.TRANSACTION || type == null)
+      return new JTAEntityManager(factory, properties, registry);
+    else {
+      _logger.error("There is currently no support for extended scope EntityManagers");
+      return null;
+    }
+
+  }
+
+  public void close() {
+    throw new UnsupportedOperationException();
+  }
+  
+  public EntityManager createEntityManager(Map arg0) {
+    throw new UnsupportedOperationException();
+  }
+
+  public Cache getCache() {
+    throw new UnsupportedOperationException();
+  }
+
+  public CriteriaBuilder getCriteriaBuilder() {
+    throw new UnsupportedOperationException();
+  }
+
+  public Metamodel getMetamodel() {
+    throw new UnsupportedOperationException();
+  }
+
+  public PersistenceUnitUtil getPersistenceUnitUtil() {
+    throw new UnsupportedOperationException();
+  }
+
+  public Map<String, Object> getProperties() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isOpen() {
+    throw new UnsupportedOperationException();
+  }
+
+}

Added: aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManager.java
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManager.java?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManager.java (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManager.java Sun Feb 27 18:08:10 2011
@@ -0,0 +1,400 @@
+/*
+ * 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.context.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+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.JTAPersistenceContextRegistry;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is responsible for managing all of the persistence contexts at a defined scope,
+ * i.e. for a single framework or composite. It will automatically manage the lifecycle of all
+ * registered persistence contexts.
+ */
+public class PersistenceContextManager extends ServiceTracker{
+  /** Logger */
+  private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container.context");
+  
+  /** The filter this tracker uses to select Persistence Units. */
+  private static final Filter filter; 
+  static {
+    Filter f = null;
+    try {
+      //Create a filter to select container managed persistence units that 
+      //are not proxies for managed persistence contexts 
+      f = FrameworkUtil.createFilter("(&(" + Constants.OBJECTCLASS
+        + "=" + "javax.persistence.EntityManagerFactory" + ")(" + 
+        PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT + "=true)(!("
+        + PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE + "=*)))" );
+    } catch (InvalidSyntaxException e) {
+      _logger.error("There was an exception creating the EntityManagerFactory filter. This should never happen.", e);
+      throw new RuntimeException(e);
+    }
+    filter = f;
+  }
+  /** A Map of bundles that reference a particular persistence unit. Only access when synchronized on <code>this</code>.*/ 
+  private final Map<String, Set<Bundle>> persistenceContextConsumers = new HashMap<String, Set<Bundle>>();
+  /** 
+   * A Map persistence unit names to persistenceContextDefinitions. We use HashMap in the generic definition to ensure that 
+   * we get the correct deep <code>equals()</code> behaviour. Only access when synchronized on <code>this</code>.
+   */
+  private final Map<String, HashMap<String, Object>> persistenceContextDefinitions = new HashMap<String, HashMap<String,Object>>();
+  /** A Map of persistence units tracked by the tracker. Only access when synchronized on <code>this</code>. */
+  private final Map<String, ServiceReference> persistenceUnits = new HashMap<String, ServiceReference>();
+  /** A Map for storing the registered ManagedPersistenceContextServiceFactory. Only access when synchronized on <code>this</code>. */
+  private final Map<String, ServiceRegistration> entityManagerRegistrations = new HashMap<String, ServiceRegistration>();
+  private final JTAPersistenceContextRegistry persistenceContextRegistry;
+  
+  /**
+   * Create a new PersistenceContextManager at a scope defined by the supplied {@link BundleContext}
+   * @param ctx the bundle context to use for tracking services. In order to prevent this
+   *            object becoming prematurely invalid it is best to use the {@link BundleContext} of
+   *            the system bundle (Bundle 0).
+   */
+  public PersistenceContextManager(BundleContext ctx, JTAPersistenceContextRegistry registry) {
+
+    super(ctx, filter, null);
+    persistenceContextRegistry = registry;
+  }
+  
+  @Override
+  public void close() {
+    super.close();
+    for (ServiceRegistration reg : entityManagerRegistrations.values()) {
+      try {
+        reg.unregister();
+      } catch (IllegalStateException ise) {
+        //This is no worry, the framework has done our job for us
+      }
+    }
+  }
+
+  @Override
+  public Object addingService(ServiceReference reference) {
+
+    if(_logger.isDebugEnabled()) {
+      _logger.debug("A new managed persistence unit, {}, has been detected.", new Object[] {reference});
+    }
+    
+    String unitName = (String) reference.getProperty(PersistenceUnitConstants.OSGI_UNIT_NAME);
+    if(unitName == null)
+      unitName = "";
+    boolean register;
+    //Use a synchronized block to ensure that we get an atomic view of the persistenceUnits
+    //and the persistenceContextDefinitions
+    synchronized (this) {
+      //If we already track a unit with the same name then we are in trouble!
+      //only one unit with a given name should exist at a single scope
+      if(persistenceUnits.containsKey(unitName)) {
+        _logger.warn("The persistence unit {} exists twice at the same framework scope. " +
+        		"The second service will be ignored", new Object[] {reference});
+        return null;
+      }
+      //If this is a new unit, then add it, and check whether we have any waiting
+      //persistenceContextDefinitions
+      persistenceUnits.put(unitName, reference);
+      register = persistenceContextDefinitions.containsKey(unitName);
+    }
+    //If there are persistenceContexts then register them
+    if(register){
+      registerEM(unitName);
+    }
+    return reference;
+  }
+
+  public void removedService(ServiceReference ref, Object o)
+  {
+    if(_logger.isDebugEnabled()) {
+      _logger.debug("A managed persistence unit, {}, has been unregistered.", new Object[] {ref});
+    }
+    
+    String unitName = (String) ref.getProperty(PersistenceUnitConstants.OSGI_UNIT_NAME);
+    if(unitName == null)
+      unitName = "";
+    //Remove the persistence Unit service to prevent other people from trying to use it
+    synchronized (this) {
+      persistenceUnits.remove(unitName);
+    }
+    //Unregister any dependent contexts
+    unregisterEM(unitName);
+  }
+
+  /**
+   * Register a persistence context definition with this manager
+   * @param name  The name of the persistence unit for this context
+   * @param client The {@link Bundle} that uses this persistence context
+   * @param properties The Map of properties for this persistence context
+   *                   This must contain the {@link PersistenceContextType}
+   */
+  public void registerContext(String name, Bundle client, HashMap<String, Object> properties) {
+    if (_logger.isDebugEnabled()) {
+      _logger.debug("Registering bundle {} as a client of persistence unit {} with properties {}.", 
+          new Object[] {client.getSymbolicName() + "_" + client.getVersion(), name, properties});
+    }
+    HashMap<String, Object> oldProps;
+    boolean register;
+    //Use a synchronized to get an atomic view
+    synchronized (this) {
+      //Add a new consumer for the context, including the Set if necessary
+      Set<Bundle> bundles = persistenceContextConsumers.get(name);
+      if(bundles == null) {
+        bundles = new HashSet<Bundle>();
+        persistenceContextConsumers.put(name, bundles);
+      }
+      bundles.add(client);
+      
+      //Check that we don't have different properties to other clients.
+      //This would not make sense, as all clients should share the same
+      //context!
+      oldProps = persistenceContextDefinitions.put(name, properties);
+      if(oldProps != null) {
+        if(!!!oldProps.equals(properties)) {
+          _logger.warn("The bundle {} depends on a managed persistence context {} with properties {}, but the context already exists with properties {}. The existing properties will be used.", 
+          new Object[] {client.getSymbolicName() + "_" + client.getVersion(), name, properties, oldProps});
+          persistenceContextDefinitions.put(name, oldProps);
+        }
+      }
+      //We should only register if our persistence unit exists
+      register = persistenceUnits.containsKey(name);
+    }
+      
+    if(register) {
+      registerEM(name);
+    }
+  }
+
+  /**
+   * Unregister the supplied bundle as a client of the supplied persistence context
+   * @param name  The name of the context
+   * @param client The bundle that is using the persistence context
+   */
+  public void unregisterContext(String name, Bundle client)
+  {
+    if (_logger.isDebugEnabled()) {
+      _logger.debug("Unregistering the bundle {} as a client of persistence unit {}.", 
+          new Object[] {client.getSymbolicName() + "_" + client.getVersion(), name});
+    }
+    boolean unregister = false;
+    //Keep an atomic view of our state
+    synchronized (this) {
+      //Remove the client
+      Set<Bundle> clients = persistenceContextConsumers.get(name);
+      clients.remove(client);
+      //If no clients remain then tidy up the context
+      if(clients.isEmpty()) {
+        persistenceContextDefinitions.remove(name);
+        persistenceContextConsumers.remove(name);
+        unregister = true;
+      }
+    }
+    //Unregister the context if it is no longer used
+    if(unregister)
+      unregisterEM(name);
+  }
+
+  /**
+   * Register a {@link ManagedPersistenceContextFactory} for the named persistence context
+   * 
+   * This must <b>never</b> be called from a <code>synchronized</code> block.
+   * @param name
+   */
+  private void registerEM(String name) {
+    
+    EntityManagerFactory entityManagerServiceFactory;
+    ServiceReference unit;
+    ServiceRegistration reg = null;
+    boolean alreadyRegistered = false;
+    try
+    {
+      //Synchronize for an atomic view
+      synchronized (this) {
+        //Not our job to register if someone is already doing it, or has already done it
+        if(entityManagerRegistrations.containsKey(name)){
+          alreadyRegistered = true;
+          return;
+        }
+        if(_logger.isDebugEnabled()) {
+          _logger.debug("Registering a managed persistence context for persistence unit {}", new Object[] {name});
+        }
+        //Block other threads from trying to register by adding the key
+        entityManagerRegistrations.put(name, null);
+        
+        //Get hold of the information we need to create the context
+        unit = persistenceUnits.get(name);
+        Map<String, Object> props = persistenceContextDefinitions.get(name);
+        
+        //If either of these things is undefined then the context cannot be registered
+        if(props == null || unit == null) {
+          _logger.error("The managed persistence context {} cannot be registered for persistence unit {} and properties {}.",
+              new Object[] {name, unit, props});
+          //The finally block will clear the entityManagerRegistrations key
+          return;
+        }
+
+        //Create the service factory
+        entityManagerServiceFactory = new ManagedPersistenceContextFactory(unit, props, persistenceContextRegistry);
+      }
+     
+      //Always register from outside a synchronized 
+      Hashtable<String, Object> props = new Hashtable<String, Object>();
+      
+      props.put(PersistenceUnitConstants.OSGI_UNIT_NAME, name);
+      props.put(PersistenceUnitConstants.OSGI_UNIT_VERSION, unit.getProperty(PersistenceUnitConstants.OSGI_UNIT_VERSION));
+      props.put(PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT, Boolean.TRUE);
+      props.put(PersistenceUnitConstants.OSGI_UNIT_PROVIDER, unit.getProperty(PersistenceUnitConstants.OSGI_UNIT_PROVIDER));
+      props.put(PersistenceUnitConstants.EMPTY_PERSISTENCE_UNIT_NAME, "".equals(name));
+      props.put(PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE, "true");
+      
+      BundleContext persistenceBundleContext = unit.getBundle().getBundleContext();
+      reg = persistenceBundleContext.registerService(
+          EntityManagerFactory.class.getName(), entityManagerServiceFactory, props);
+    } finally {
+      //As we have to register from outside a synchronized then someone may be trying to
+      //unregister us. They will try to wait for us to finish, but in order to prevent 
+      //live-lock they may flag to us that we need to do the unregistration by removing
+      //the persistence context key.
+      boolean recoverFromLiveLock = false;
+    
+      //Synchronize to get an atomic view
+      synchronized (this) {
+        //If we created a registration
+        if(reg != null) {
+          //If the key still exists then all is well
+          if(entityManagerRegistrations.containsKey(name)) {
+            entityManagerRegistrations.put(name, reg);
+          } else {
+            //Else we were in a potential live-lock and the service could not be unregistered
+            //earlier. This means we have to do it (but outside the synchronized. Make sure we
+            //also remove the registration key!
+            _logger.warn("Recovering from a potential live-lock registering a container managed peristence context for persistence unit {}.",
+                new Object[] {name});
+            entityManagerRegistrations.remove(name);
+            recoverFromLiveLock = true;
+          }
+        }
+        //There was no registration created. Remove the key if we were registering.
+        else {
+          if(!!!alreadyRegistered)
+            entityManagerRegistrations.remove(name);
+        }
+        
+        //Notify any waiting unregistrations that they can proceed
+        this.notifyAll();
+      }
+      //If we were live-locked then unregister the registration here
+      if(recoverFromLiveLock)
+        reg.unregister();
+    }
+  }
+  
+  /**
+   * Unregister the named persistence context. This code attempts to ensure that
+   * the calling thread performs the unregistration to ensure the calls are
+   * synchronous. If a potential live-lock is detected then the unregistration may
+   * occur on a different thread or at a different time.
+   * 
+   * This must <b>never</b> be called from a <code>synchronized</code> block.
+   * @param unitName
+   * 
+   */
+  private void unregisterEM(String unitName) {
+    ServiceRegistration reg = null;
+    //Look for the registration
+    synchronized (this) {
+      //We use a loop to prevent live-locking, we
+      //loop a maximum of 4 times before
+      boolean found = false;
+      int tries = 0;
+      while (!!!found && tries < 4) {
+        //If we contain the key then get the registration,
+        //If not, then we have nothing to unregister
+        if(entityManagerRegistrations.containsKey(unitName))
+          reg = entityManagerRegistrations.get(unitName);
+        else
+          return;
+        
+        //It is possible that the registration is null. This means we are in
+        //the transient case where a registration is being created. If we wait
+        //then everything should be fine. If the registration is not null, then
+        //remove it from the map and escape the loop
+        if(reg != null) {
+          found = true;
+          entityManagerRegistrations.remove(unitName);
+        } else
+          //We didn't find it, wait and try again
+          try {
+            this.wait(500);
+          } catch (InterruptedException e) {
+            _logger.warn("The Aries JPA container was interrupted when waiting for managed persistence context {} to be unregistered", new Object[] {unitName});
+          }
+        //Increment the loop to prevent us from live-locking
+        tries++;
+      }
+      //If we didn't find the registration in the loop then still
+      //remove the key to warn the registering thread to unregister
+      //immediately.
+      if(!found) {
+        //Possible Live lock, just remove the key
+        entityManagerRegistrations.remove(unitName);
+        _logger.warn("The JPA container detected a possible live lock whilst unregistering the managed persistence context {}. The service cannot be unregistered immediately so the context may become unusable before being unregistered.",
+            new Object[] {unitName});
+      }
+    }
+    //If we found the registration then unregister it outside the synchronized.
+    if (reg != null) {
+      reg.unregister();
+    }
+  }
+
+  @Override
+  /**
+   * Open this <code>PersistenceContextManager</code> and begin tracking services.
+   * 
+   * <p>
+   * This implementation calls <code>open(true)</code>.
+   * 
+   * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
+   *         with which this <code>ServiceTracker</code> was created is no
+   *         longer valid.
+   * @see #open(boolean)
+   */
+  public void open() {
+    super.open(true);
+  }
+}

Added: aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAEntityManager.java Sun Feb 27 18:08:10 2011
@@ -0,0 +1,354 @@
+/*
+ * 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.context.transaction.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.TransactionRequiredException;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.metamodel.Metamodel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A <code>PersistenceContextType.TRANSACTION</code> {@link EntityManager} instance
+ */
+public class JTAEntityManager implements EntityManager {
+  /** Logger */
+  private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container.context");
+  
+  /** The {@link EntityManagerFactory} that can create new {@link EntityManager} instances */
+  private final EntityManagerFactory emf;
+  /** The map of properties to pass when creating EntityManagers */
+  private final Map<String, Object> props;
+  /** A registry for creating new persistence contexts */
+  private final JTAPersistenceContextRegistry reg;
+  /** 
+   * The entity manager to use when there is no transaction. Note that there is one of these
+   * per injection site.
+   */
+  private EntityManager detachedManager = null;
+  
+  public JTAEntityManager(EntityManagerFactory factory,
+      Map<String, Object> properties, JTAPersistenceContextRegistry registry) {
+    emf = factory;
+    props = properties;
+    reg = registry;
+  }
+
+  /**
+   * Get the target persistence context
+   * @param forceTransaction Whether the returned entity manager needs to be bound to a transaction
+   * @throws TransactionRequiredException if forceTransaction is true and no transaction is available
+   * @return
+   */
+  private EntityManager getPersistenceContext(boolean forceTransaction) 
+  {
+    if (forceTransaction) {
+      return reg.getCurrentPersistenceContext(emf, props);
+    } else {
+      if (reg.isTransactionActive()) {
+        return reg.getCurrentPersistenceContext(emf, props);
+      } else {
+        if(!!!reg.jtaIntegrationAvailable() && _logger.isDebugEnabled())
+          _logger.debug("No integration with JTA transactions is available. No transaction context is active.");
+        
+        if (detachedManager == null) {
+          EntityManager temp = emf.createEntityManager(props);
+          
+          synchronized (this) {
+            if (detachedManager == null) {
+              detachedManager = temp;
+              temp = null;
+            }
+          }
+          
+          if (temp != null)
+            temp.close();
+        }
+        return detachedManager;
+      }
+    }
+  }
+  
+  /**
+   * Called reflectively by blueprint
+   */
+  public void internalClose() {
+    EntityManager temp = null;
+    
+    synchronized (this) {
+      temp = detachedManager;
+      detachedManager = null;
+    }
+    
+    if (temp != null)
+      temp.close();
+  }
+  
+  public void clear()
+  {
+    getPersistenceContext(false).clear();
+  }
+
+  public void close()
+  {
+    //TODO add a message here
+    throw new IllegalStateException("It is forbidden to call close on a container managed EntityManager");
+  }
+
+  public boolean contains(Object arg0)
+  {
+    return getPersistenceContext(false).contains(arg0);
+  }
+
+  public Query createNamedQuery(String arg0)
+  {
+    return getPersistenceContext(false).createNamedQuery(arg0);
+  }
+
+  public Query createNativeQuery(String arg0)
+  {
+    return getPersistenceContext(false).createNativeQuery(arg0);
+  }
+
+  @SuppressWarnings("unchecked")
+  public Query createNativeQuery(String arg0, Class arg1)
+  {
+    return getPersistenceContext(false).createNativeQuery(arg0, arg1);
+  }
+
+  public Query createNativeQuery(String arg0, String arg1)
+  {
+    return getPersistenceContext(false).createNativeQuery(arg0, arg1);
+  }
+
+  public Query createQuery(String arg0)
+  {
+    return getPersistenceContext(false).createQuery(arg0);
+  }
+
+  public <T> T find(Class<T> arg0, Object arg1)
+  {
+    return getPersistenceContext(false).find(arg0, arg1);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void flush()
+  {
+    getPersistenceContext(true).flush();
+  }
+
+  public Object getDelegate()
+  {
+    return getPersistenceContext(false).getDelegate();
+  }
+
+  public FlushModeType getFlushMode()
+  {
+    return getPersistenceContext(false).getFlushMode();
+  }
+
+  public <T> T getReference(Class<T> arg0, Object arg1)
+  {
+    return getPersistenceContext(false).getReference(arg0, arg1);
+  }
+
+  public EntityTransaction getTransaction()
+  {
+    throw new IllegalStateException("Transaction management is not available for container managed EntityManagers");
+  }
+
+  public boolean isOpen()
+  {
+    return true;
+  }
+
+  public void joinTransaction()
+  {
+    //This should be a no-op for a JTA entity manager
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void lock(Object arg0, LockModeType arg1)
+  {
+    getPersistenceContext(true).lock(arg0, arg1);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public <T> T merge(T arg0)
+  {
+    return getPersistenceContext(true).merge(arg0);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void persist(Object arg0)
+  {
+    getPersistenceContext(true).persist(arg0);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void refresh(Object arg0)
+  {
+    getPersistenceContext(true).refresh(arg0);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void remove(Object arg0)
+  {
+    getPersistenceContext(true).remove(arg0);
+  }
+
+  public void setFlushMode(FlushModeType arg0)
+  {
+    getPersistenceContext(false).setFlushMode(arg0);
+  }
+
+  public <T> TypedQuery<T> createNamedQuery(String arg0, Class<T> arg1)
+  {
+    return getPersistenceContext(false).createNamedQuery(arg0, arg1);
+  }
+
+  public <T> TypedQuery<T> createQuery(CriteriaQuery<T> arg0)
+  {
+    return getPersistenceContext(false).createQuery(arg0);
+  }
+
+  public <T> TypedQuery<T> createQuery(String arg0, Class<T> arg1)
+  {
+    return getPersistenceContext(false).createQuery(arg0, arg1);
+  }
+
+  public void detach(Object arg0)
+  {
+    getPersistenceContext(false).detach(arg0);
+  }
+
+  public <T> T find(Class<T> arg0, Object arg1, Map<String, Object> arg2)
+  {
+    return getPersistenceContext(false).find(arg0, arg1, arg2);
+  }
+
+  /**
+   * @throws TransactionRequiredException if lock mode is not NONE
+   */
+  public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2)
+  {
+    return getPersistenceContext(arg2 != LockModeType.NONE).find(arg0, arg1, arg2);
+  }
+
+  /**
+   * @throws TransactionRequiredException if lock mode is not NONE
+   */
+  public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2, Map<String, Object> arg3)
+  {
+    return getPersistenceContext(arg2 != LockModeType.NONE).find(arg0, arg1, arg2, arg3);
+  }
+
+  public CriteriaBuilder getCriteriaBuilder()
+  {
+    return getPersistenceContext(false).getCriteriaBuilder();
+  }
+
+  public EntityManagerFactory getEntityManagerFactory()
+  {
+    return emf;
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public LockModeType getLockMode(Object arg0)
+  {
+    return getPersistenceContext(true).getLockMode(arg0);
+  }
+
+  public Metamodel getMetamodel()
+  {
+    return getPersistenceContext(false).getMetamodel();
+  }
+
+  public Map<String, Object> getProperties()
+  {
+    return getPersistenceContext(false).getProperties();
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void lock(Object arg0, LockModeType arg1, Map<String, Object> arg2)
+  {
+    getPersistenceContext(true).lock(arg0, arg1, arg2);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void refresh(Object arg0, Map<String, Object> arg1)
+  {
+    getPersistenceContext(true).refresh(arg0, arg1);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void refresh(Object arg0, LockModeType arg1)
+  {
+    getPersistenceContext(true).refresh(arg0, arg1);
+  }
+
+  /**
+   * @throws TransactionRequiredException
+   */
+  public void refresh(Object arg0, LockModeType arg1, Map<String, Object> arg2)
+  {
+    getPersistenceContext(true).refresh(arg0, arg1, arg2);
+  }
+
+  public void setProperty(String arg0, Object arg1)
+  {
+    getPersistenceContext(false).setProperty(arg0, arg1);
+  }
+
+  public <T> T unwrap(Class<T> arg0)
+  {
+    return getPersistenceContext(false).unwrap(arg0);
+  }
+}

Added: aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistry.java Sun Feb 27 18:08:10 2011
@@ -0,0 +1,226 @@
+/*
+ * 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.context.transaction.impl;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.TransactionRequiredException;
+import javax.transaction.Synchronization;
+import javax.transaction.TransactionSynchronizationRegistry;
+
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is used to manage the lifecycle of JTA peristence contexts
+ */
+public final class JTAPersistenceContextRegistry {
+  /** Logger */
+  private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container.context");
+  /** The unique key we use to find our Map */
+  private static final TSRKey EMF_MAP_KEY = new TSRKey();
+  
+  /** 
+   * A simple class to avoid key collisions in the TransactionSynchronizationRegistry. 
+   * As recommended by {@link TransactionSynchronizationRegistry#putResource(Object, Object)}
+   */
+  private final static class TSRKey {
+
+    @Override
+    public final boolean equals(Object o) {
+      return (this == o);
+    }
+
+    @Override
+    public final int hashCode() {
+      return 0xDEADBEEF;
+    }
+  }
+  
+  /** 
+   * The transaction synchronization registry, used to determine the currently
+   * active transaction, and to register for post-commit cleanup. 
+   */
+  private TransactionSynchronizationRegistry tranRegistry;
+  
+  /** 
+   * 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();
+
+  /**
+   * Get a PersistenceContext for the current transaction. The persistence context will 
+   * automatically be closed when the transaction completes.
+   * 
+   * @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.
+   * 
+   * @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
+  {
+    //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.
+    
+    //Throw the error on to the client
+    if(!!!isTransactionActive()) {
+      if(jtaIntegrationAvailable())
+        throw new TransactionRequiredException("No transaction currently active");
+      else {
+        throw new TransactionRequiredException("No JTA transaction services implementation is currently available. As a result the" +
+        		" JPA container cannot integrate with JTA transactions.");
+      }
+    }
+    EntityManager toReturn = null;
+    
+    //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);
+    
+    //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) {
+      toReturn = contextsForTransaction.get(persistenceUnit);
+    } else {
+      contextsForTransaction = new IdentityHashMap<EntityManagerFactory, EntityManager>();
+      try {
+        tranRegistry.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());
+      }
+    }
+    
+    //If we have no previously created EntityManager
+    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()});
+      try {
+        tranRegistry.registerInterposedSynchronization(new EntityManagerClearUp(toReturn));
+      } 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()});
+        toReturn.close();
+        throw new TransactionRequiredException("Unable to synchronize with transaction " + tranRegistry.getTransactionKey());
+      }
+      contextsForTransaction.put(persistenceUnit, toReturn);
+    } else {
+      if(_logger.isDebugEnabled())
+        _logger.debug("Re-using a persistence context for transaction " + tranRegistry.getTransactionKey());
+    }
+    return toReturn;
+  }
+  
+  /**
+   * Determine whether there is an active transaction on the thread
+   * @return
+   */
+  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;
+  }
+
+  /**
+   * Returns true if we have access to a {@link TransactionSynchronizationRegistry} and
+   * can manage persistence contexts
+   * @return
+   */
+  public final boolean jtaIntegrationAvailable()
+  {
+    return registryAvailable.get();
+  }
+  
+  /**
+   * Called by the blueprint container to indicate that a new {@link TransactionSynchronizationRegistry}
+   * will be used by 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});
+    }
+  }
+  
+  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.");
+  }
+  
+  /**
+   * This class is used to close EntityManager instances once the transaction has committed,
+   * and clear the persistenceContextRegistry of old persistence contexts.
+   */
+  private final static class EntityManagerClearUp implements Synchronization {
+
+    private final EntityManager context;
+    
+    /**
+     * Create a Synchronization to clear up our EntityManagers
+     * @param em
+     */
+    public EntityManagerClearUp(EntityManager em)
+    {
+      context = em;
+    }
+    
+    public final void beforeCompletion() {
+      //This is a no-op;
+    }
+
+    @SuppressWarnings("unchecked")
+    public final void afterCompletion(int arg0) {
+      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);
+      }
+    }
+  }
+}

Added: aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/resources/OSGI-INF/blueprint/jpa.xml
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/resources/OSGI-INF/blueprint/jpa.xml?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/resources/OSGI-INF/blueprint/jpa.xml (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-context/src/main/resources/OSGI-INF/blueprint/jpa.xml Sun Feb 27 18:08:10 2011
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+            xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+  
+  <service interface="org.apache.aries.jpa.container.context.PersistenceContextProvider" ref="persistenceManager"/>
+  
+  <bean id="persistenceManager" class="org.apache.aries.jpa.container.context.impl.GlobalPersistenceManager">
+    <property name="registry" ref="JtaPersistenceContextRegistry" />
+  </bean>
+  
+  <bean id="JtaPersistenceContextRegistry" 
+    class="org.apache.aries.jpa.container.context.transaction.impl.JTAPersistenceContextRegistry">
+    <property name="tranRegistry">
+      <reference interface="javax.transaction.TransactionSynchronizationRegistry"  availability="optional">
+        <reference-listener ref="JtaPersistenceContextRegistry" bind-method="addRegistry" unbind-method="removeRegistry"/>
+      </reference>
+    </property>
+  </bean>
+  
+</blueprint>
\ No newline at end of file

Added: aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/impl/PersistenceContextManagerTest.java Sun Feb 27 18:08:10 2011
@@ -0,0 +1,288 @@
+/*
+ * 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 static java.lang.Boolean.TRUE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.apache.aries.jpa.container.PersistenceUnitConstants;
+import org.apache.aries.jpa.container.context.PersistenceContextProvider;
+import org.apache.aries.mocks.BundleContextMock;
+import org.apache.aries.mocks.BundleMock;
+import org.apache.aries.unittest.mocks.Skeleton;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Version;
+
+
+public class PersistenceContextManagerTest {
+
+  private BundleContext context;
+  private PersistenceContextManager mgr;
+  
+  private EntityManagerFactory emf1;
+  private EntityManagerFactory emf2;
+  
+  private ServiceRegistration reg1;
+  private ServiceRegistration reg2;
+  
+  private Bundle client1;
+  private Bundle client2;
+  
+  @Before
+  public void setUp()
+  {
+    client1 = Skeleton.newMock(Bundle.class);
+    client2 = Skeleton.newMock(Bundle.class);
+    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.open();
+  }
+  
+  @After
+  public void tearDown()
+  {
+    BundleContextMock.clear();
+  }
+  
+  /**
+   * A simple test to show we get a service registered when a unit
+   * is registered first.
+   * 
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testUnitThenContext() throws InvalidSyntaxException
+  {
+    String unitName = "unit";
+    
+    reg1 = registerUnit(emf1, unitName, TRUE);
+    
+    assertNoContextRegistered();
+    
+    mgr.registerContext(unitName, client1, new HashMap<String, Object>());
+    
+    assertContextRegistered(unitName);
+  }
+
+  /**
+   * A simple test to show we get a service unregistered when a context
+   * is removed.
+   * 
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testUnitThenContextThenRemoveContext() throws InvalidSyntaxException
+  {
+    testUnitThenContext();
+    mgr.unregisterContext("unit", client1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * A simple test to show we get a service unregistered when a unit
+   * is removed.
+   * 
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testUnitThenContextThenRemoveUnit() throws InvalidSyntaxException
+  {
+    testUnitThenContext();
+    reg1.unregister();
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * A simple test to show we get a service registered when a context
+   * is registered first.
+   * 
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testContextThenUnit() throws InvalidSyntaxException
+  {
+    String unitName = "unit";
+    
+    mgr.registerContext(unitName, client1, new HashMap<String, Object>());
+    
+    assertNoContextRegistered();
+    
+    reg1 = registerUnit(emf1, unitName, TRUE);
+    
+    assertContextRegistered(unitName);
+  }
+
+  /**
+   * A simple test to show we get a service unregistered when a context
+   * is removed.
+   * 
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testContextThenUnitThenRemoveContext() throws InvalidSyntaxException
+  {
+    testContextThenUnit();
+    mgr.unregisterContext("unit", client1);
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * A simple test to show we get a service unregistered when a unit
+   * is removed.
+   * 
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testContextThenUnitThenRemoveUnit() throws InvalidSyntaxException
+  {
+    testContextThenUnit();
+    reg1.unregister();
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we don't register a service when the unit and
+   * context don't match
+   */
+  @Test
+  public void testAddDifferentContext() throws InvalidSyntaxException
+  {
+    reg1 = registerUnit(emf1, "unit", TRUE);
+    
+    assertNoContextRegistered();
+    
+    mgr.registerContext("context", client1, new HashMap<String, Object>());
+    
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we don't unregister a service when a different context is
+   * removed
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testRemoveDifferentContext() throws InvalidSyntaxException
+  {
+    testAddDifferentContext();
+    
+    mgr.registerContext("unit", client1, new HashMap<String, Object>());
+    
+    assertContextRegistered("unit");
+    
+    mgr.unregisterContext("context", client1);
+    
+    assertContextRegistered("unit");
+  }
+  
+  /**
+   * Test that we don't unregister a service when a different unit is
+   * removed
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testRemoveDifferentUnit() throws InvalidSyntaxException
+  {
+    testAddDifferentContext();
+    reg2 = registerUnit(emf2, "context", TRUE);
+    assertContextRegistered("context");
+    reg1.unregister();
+    assertContextRegistered("context");
+    reg2.unregister();
+    assertNoContextRegistered();
+  }
+  
+  /**
+   * Test that we cope when multiple clients consume the same context
+   * @throws InvalidSyntaxException
+   */
+  @Test
+  public void testMultipleClients() throws InvalidSyntaxException
+  {
+    testContextThenUnit();
+    
+    mgr.registerContext("unit", client2, new HashMap<String, Object>());
+    assertContextRegistered("unit");
+    
+    mgr.unregisterContext("unit", client1);
+    assertContextRegistered("unit");
+    
+    mgr.unregisterContext("unit", client2);
+    assertNoContextRegistered();
+  }
+  
+  
+  private ServiceRegistration registerUnit(EntityManagerFactory emf, String name, Boolean managed) {
+    
+    Hashtable<String, Object> props = new Hashtable<String, Object>();
+    
+    if(name != null)
+      props.put(PersistenceUnitConstants.OSGI_UNIT_NAME, name);
+    
+    if(managed)
+      props.put(PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT, managed);
+    
+    props.put(PersistenceUnitConstants.OSGI_UNIT_PROVIDER, "some.provider.Name");
+    props.put(PersistenceUnitConstants.OSGI_UNIT_VERSION, new Version("1.0.0"));
+    
+    return context.registerService(
+        EntityManagerFactory.class.getName(), emf, props);
+  }
+  
+  private void assertNoContextRegistered() throws InvalidSyntaxException {
+    ServiceReference[] refs = context.getServiceReferences(EntityManagerFactory.class.getName(), "("+PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE+"=*)");
+
+    assertNull(refs);
+  }
+  
+  private void assertContextRegistered(String name) throws InvalidSyntaxException {
+    BundleContextMock.assertServiceExists(EntityManagerFactory.class.getName());
+    
+    ServiceReference[] refs = context.getServiceReferences(EntityManagerFactory.class.getName(), "("+PersistenceContextProvider.PROXY_FACTORY_EMF_ATTRIBUTE+"=*)");
+    
+    assertEquals("Too many EntityManagerFactories", 1, refs.length);
+    
+    assertEquals("Wrong unit name", name, refs[0].getProperty(PersistenceUnitConstants.OSGI_UNIT_NAME));
+    
+    assertEquals("Wrong provider name", "some.provider.Name", refs[0].getProperty(PersistenceUnitConstants.OSGI_UNIT_PROVIDER));
+    
+    assertEquals("Wrong unit version", new Version("1.0.0"), refs[0].getProperty(PersistenceUnitConstants.OSGI_UNIT_VERSION));
+    
+    assertEquals("Unit should be managed", Boolean.TRUE, refs[0].getProperty(PersistenceUnitConstants.CONTAINER_MANAGED_PERSISTENCE_UNIT));
+  }
+}

Added: aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-context/src/test/java/org/apache/aries/jpa/container/context/transaction/impl/JTAPersistenceContextRegistryTest.java Sun Feb 27 18:08:10 2011
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 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 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;
+
+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;
+  private Map<Object,Object> props1;
+  private EntityManagerFactory emf2;
+  private Map<Object,Object> props2;
+  
+  private JTAPersistenceContextRegistry contexts;
+  
+  @Before
+  public void setup() 
+  {
+    reg = new TranSyncRegistryMock();
+
+    props1 = new HashMap<Object, Object>();
+    props1.put("prop1", "value1");
+    
+    props2 = new HashMap<Object, Object>();
+    props2.put("prop2", "value2");
+    
+    emf1 = Skeleton.newMock(EntityManagerFactory.class);
+    
+    Skeleton.getSkeleton(emf1).setReturnValue(new MethodCall(EntityManagerFactory.class, 
+        "createEntityManager", props1), Skeleton.newMock(EntityManager.class));
+    Skeleton.getSkeleton(emf1).setReturnValue(new MethodCall(EntityManagerFactory.class, 
+        "createEntityManager", props2), Skeleton.newMock(EntityManager.class));
+    
+    emf2 = Skeleton.newMock(EntityManagerFactory.class);
+
+    Skeleton.getSkeleton(emf2).setReturnValue(new MethodCall(EntityManagerFactory.class, 
+        "createEntityManager", props1), Skeleton.newMock(EntityManager.class));
+    Skeleton.getSkeleton(emf2).setReturnValue(new MethodCall(EntityManagerFactory.class, 
+        "createEntityManager", props2), Skeleton.newMock(EntityManager.class));
+
+    
+    contexts = new JTAPersistenceContextRegistry();
+    contexts.setTranRegistry(Skeleton.newMock(reg, TransactionSynchronizationRegistry.class));
+    contexts.addRegistry(null);
+  }
+  
+  @Test
+  public void testIsTranActive()
+  {
+    reg.setTransactionKey(null);
+    
+    assertFalse(contexts.isTransactionActive());
+    
+    reg.setTransactionKey("");
+    
+    assertTrue(contexts.isTransactionActive());
+  }
+  
+  @Test
+  public void testMultiGetsOneTran()
+  {
+    reg.setTransactionKey("");
+    
+    EntityManager em1a = contexts.getCurrentPersistenceContext(emf1, props1);
+    EntityManager em1b = contexts.getCurrentPersistenceContext(emf1, props1);
+    
+    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);
+    
+    EntityManager em2a = contexts.getCurrentPersistenceContext(emf2, props1);
+    EntityManager em2b = contexts.getCurrentPersistenceContext(emf2, props1);
+    
+    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);
+    
+    reg.afterCompletion("");
+    
+    Skeleton.getSkeleton(em1a).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"),1);
+    Skeleton.getSkeleton(em2a).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"),1);
+  }
+  
+  @Test
+  public void testMultiGetsMultiTrans()
+  {
+    reg.setTransactionKey("a");
+    EntityManager em1a = contexts.getCurrentPersistenceContext(emf1, props1);
+    reg.setTransactionKey("b");
+    EntityManager em1b = contexts.getCurrentPersistenceContext(emf1, props2);
+    
+    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"));
+    
+    reg.setTransactionKey("a");
+    EntityManager em2a = contexts.getCurrentPersistenceContext(emf2, props1);
+    reg.setTransactionKey("b");
+    EntityManager em2b = contexts.getCurrentPersistenceContext(emf2, props2);
+    
+    Skeleton.getSkeleton(emf2).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props1), 1);
+    Skeleton.getSkeleton(emf2).assertCalledExactNumberOfTimes(new MethodCall(EntityManagerFactory.class, "createEntityManager", props2), 1);
+   
+    assertNotSame("We should get the same delegate!", em2a, em2b);
+    
+    Skeleton.getSkeleton(em2a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
+    Skeleton.getSkeleton(em2b).assertNotCalled(new MethodCall(EntityManager.class, "close"));
+    
+    reg.setTransactionKey("b");
+    reg.afterCompletion("b");
+    
+    Skeleton.getSkeleton(em1a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
+    Skeleton.getSkeleton(em1b).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"), 1);
+    Skeleton.getSkeleton(em2a).assertNotCalled(new MethodCall(EntityManager.class, "close"));
+    Skeleton.getSkeleton(em2b).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"), 1);
+    
+    reg.setTransactionKey("a");
+    reg.afterCompletion("a");
+    
+    Skeleton.getSkeleton(em1a).assertCalledExactNumberOfTimes(new MethodCall(EntityManager.class, "close"), 1);
+    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);
+  }
+  
+  @Test
+  public void testNoTranSyncRegistry() {
+    JTAPersistenceContextRegistry registry = new JTAPersistenceContextRegistry();
+    //blueprint will still call our setter
+    TransactionSynchronizationRegistry tranSyncReg = Skeleton.newMock(reg, TransactionSynchronizationRegistry.class);
+    registry.setTranRegistry(tranSyncReg);
+    
+    reg.setTransactionKey(null);
+    
+    assertFalse(registry.jtaIntegrationAvailable());
+    assertFalse(registry.isTransactionActive());
+    
+    Skeleton.getSkeleton(tranSyncReg).assertSkeletonNotCalled();
+    
+    reg.setTransactionKey("");
+    
+    assertFalse(registry.jtaIntegrationAvailable());
+    assertFalse(registry.isTransactionActive());
+    
+    Skeleton.getSkeleton(tranSyncReg).assertSkeletonNotCalled();
+  }
+  
+  @Test(expected=TransactionRequiredException.class)
+  public void testGetNoTran() {
+    reg.setTransactionKey(null);
+    contexts.getCurrentPersistenceContext(emf1, props1);
+  }
+  
+  @Test(expected=TransactionRequiredException.class)
+  public void testGetNoTranSyncRegistry() {
+    reg.setTransactionKey("");
+    contexts.removeRegistry(null);
+    contexts.getCurrentPersistenceContext(emf1, props1);
+  }
+  
+}

Added: aries/tags/jpa-0.1-incubating/jpa-container-itest/pom.xml
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-itest/pom.xml?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-itest/pom.xml (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-itest/pom.xml Sun Feb 27 18:08:10 2011
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>jpa</artifactId>
+        <groupId>org.apache.aries.jpa</groupId>
+        <version>0.1-incubating</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>org.apache.aries.jpa.container.itest</artifactId>
+    <name>Aries JPA iTests</name>
+    <!--
+
+      For adding EclipseLink as a provider
+
+   <repositories>
+      <repository>
+        <id>EclipseLink Repo</id>
+        <url>http://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo</url>
+      </repository>
+    </repositories>
+    -->
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.logging</groupId>
+            <artifactId>pax-logging-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.logging</groupId>
+            <artifactId>pax-logging-service</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-default</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries</groupId>
+            <artifactId>org.apache.aries.util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.blueprint</groupId>
+            <artifactId>org.apache.aries.blueprint</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.url</groupId>
+            <artifactId>pax-url-mvn</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configadmin</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jpa_2.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <artifactId>org.apache.aries.jpa.api</artifactId>
+            <groupId>org.apache.aries.jpa</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jpa</groupId>
+            <artifactId>org.apache.aries.jpa.container</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jpa</groupId>
+            <artifactId>org.apache.aries.jpa.container.context</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jpa</groupId>
+            <artifactId>org.apache.aries.jpa.blueprint.aries</artifactId>
+            <!--<scope>test</scope>-->
+        </dependency>
+        <!--
+
+        For adding EclipseLink as a provider
+
+     <dependency>
+          <groupId>org.eclipse.persistence</groupId>
+          <artifactId>org.eclipse.persistence.jpa</artifactId>
+          <version>2.0.0</version>
+          <scope>test</scope>
+        </dependency>
+        <dependency>
+          <groupId>org.eclipse.persistence</groupId>
+          <artifactId>org.eclipse.persistence.core</artifactId>
+          <version>2.0.0</version>
+          <scope>test</scope>
+        </dependency>
+        <dependency>
+          <groupId>org.eclipse.persistence</groupId>
+          <artifactId>org.eclipse.persistence.asm</artifactId>
+          <version>2.0.0</version>
+          <scope>test</scope>
+        </dependency>
+        -->
+        <dependency>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jta_1.1_spec</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-pool</groupId>
+            <artifactId>commons-pool</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.serp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jpa</groupId>
+            <artifactId>org.apache.aries.jpa.container.itest.bundle</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jpa</groupId>
+            <artifactId>org.apache.aries.jpa.blueprint.itest.bundle</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.servicemix.tooling</groupId>
+                <artifactId>depends-maven-plugin</artifactId>
+                <version>1.1</version>
+                <executions>
+                    <execution>
+                        <id>generate-depends-file</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>ci-build-profile</id>
+            <activation>
+                <property>
+                    <name>maven.repo.local</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <!--
+                              when the local repo location has been specified, we need
+                              to pass on this information to PAX mvn url
+                            -->
+                            <argLine>-Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local}
+                            </argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>

Added: aries/tags/jpa-0.1-incubating/jpa-container-itest/src/test/java/org/apache/aries/jpa/blueprint/aries/itest/JPAInjectionTest.java
URL: http://svn.apache.org/viewvc/aries/tags/jpa-0.1-incubating/jpa-container-itest/src/test/java/org/apache/aries/jpa/blueprint/aries/itest/JPAInjectionTest.java?rev=1075099&view=auto
==============================================================================
--- aries/tags/jpa-0.1-incubating/jpa-container-itest/src/test/java/org/apache/aries/jpa/blueprint/aries/itest/JPAInjectionTest.java (added)
+++ aries/tags/jpa-0.1-incubating/jpa-container-itest/src/test/java/org/apache/aries/jpa/blueprint/aries/itest/JPAInjectionTest.java Sun Feb 27 18:08:10 2011
@@ -0,0 +1,189 @@
+/*  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.blueprint.aries.itest;
+
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.equinox;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
+import static org.ops4j.pax.exam.OptionUtils.combine;
+
+import org.apache.aries.jpa.blueprint.itest.JPATestBean;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Inject;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.exam.options.BootDelegationOption;
+import org.ops4j.pax.exam.options.MavenArtifactProvisionOption;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.Version;
+import org.osgi.util.tracker.ServiceTracker;
+
+@RunWith(JUnit4TestRunner.class)
+public class JPAInjectionTest {
+  public static final long DEFAULT_TIMEOUT = 30000;
+
+  @Inject
+  protected BundleContext bundleContext;
+ 
+  @Test
+  public void findResources() throws Exception {
+    JPATestBean bean = getOsgiService(JPATestBean.class);
+    
+    assertTrue("No persistence unit injection", bean.pUnitAvailable());
+    assertTrue("No persistence context injection", bean.pContextAvailable());
+  }
+
+  @org.ops4j.pax.exam.junit.Configuration
+  public static Option[] configuration() {
+    Option[] options = options(
+        bootDelegation(),
+        
+        // Log
+        mavenBundle("org.ops4j.pax.logging", "pax-logging-api"),
+        mavenBundle("org.ops4j.pax.logging", "pax-logging-service"),
+        // Felix Config Admin
+        mavenBundle("org.apache.felix", "org.apache.felix.configadmin"),
+        // Felix mvn url handler
+        mavenBundle("org.ops4j.pax.url", "pax-url-mvn"),
+
+        // this is how you set the default log level when using pax
+        // logging (logProfile)
+        systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("DEBUG"),
+
+        // 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"),
+        mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.container.context"),
+        mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.blueprint.aries"),
+        mavenBundle("org.apache.geronimo.specs", "geronimo-jta_1.1_spec"),
+        mavenBundle("commons-lang", "commons-lang"),
+        mavenBundle("commons-collections", "commons-collections"),
+        mavenBundle("commons-pool", "commons-pool"),
+        mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.serp"),
+        mavenBundle("org.apache.openjpa", "openjpa"),
+
+//        mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.jpa"),
+//        mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.core"),
+//        mavenBundle("org.eclipse.persistence", "org.eclipse.persistence.asm"),
+        
+        mavenBundle("org.apache.aries.jpa", "org.apache.aries.jpa.blueprint.itest.bundle"),
+        
+        equinox().version("3.5.0"));
+    options = updateOptions(options);
+    return options;
+  }
+  
+  
+  protected Bundle getBundle(String symbolicName) {
+    return getBundle(symbolicName, null);
+  }
+
+  protected Bundle getBundle(String bundleSymbolicName, String version) {
+    Bundle result = null;
+    for (Bundle b : bundleContext.getBundles()) {
+      if (b.getSymbolicName().equals(bundleSymbolicName)) {
+        if (version == null
+            || b.getVersion().equals(Version.parseVersion(version))) {
+          result = b;
+          break;
+        }
+      }
+    }
+    return result;
+  }
+
+  public static BootDelegationOption bootDelegation() {
+    return new BootDelegationOption("org.apache.aries.unittest.fixture");
+  }
+  
+  public static MavenArtifactProvisionOption mavenBundle(String groupId,
+      String artifactId) {
+    return CoreOptions.mavenBundle().groupId(groupId).artifactId(artifactId)
+        .versionAsInProject();
+  }
+
+  protected static Option[] updateOptions(Option[] options) {
+    // We need to add pax-exam-junit here when running with the ibm
+    // jdk to avoid the following exception during the test run:
+    // ClassNotFoundException: org.ops4j.pax.exam.junit.Configuration
+    if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
+      Option[] ibmOptions = options(wrappedBundle(mavenBundle(
+          "org.ops4j.pax.exam", "pax-exam-junit")));
+      options = combine(ibmOptions, options);
+    }
+
+    return options;
+  }
+
+  protected <T> T getOsgiService(Class<T> type, long timeout) {
+    return getOsgiService(type, null, timeout);
+  }
+
+  protected <T> T getOsgiService(Class<T> type) {
+    return getOsgiService(type, null, DEFAULT_TIMEOUT);
+  }
+  
+  protected <T> T getOsgiService(Class<T> type, String filter, long timeout) {
+    return getOsgiService(null, type, filter, timeout);
+  }
+
+  protected <T> T getOsgiService(BundleContext bc, Class<T> type,
+      String filter, long timeout) {
+    ServiceTracker tracker = null;
+    try {
+      String flt;
+      if (filter != null) {
+        if (filter.startsWith("(")) {
+          flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")"
+              + filter + ")";
+        } else {
+          flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")("
+              + filter + "))";
+        }
+      } else {
+        flt = "(" + Constants.OBJECTCLASS + "=" + type.getName() + ")";
+      }
+      Filter osgiFilter = FrameworkUtil.createFilter(flt);
+      tracker = new ServiceTracker(bc == null ? bundleContext : bc, osgiFilter,
+          null);
+      tracker.open();
+      // Note that the tracker is not closed to keep the reference
+      // This is buggy, has the service reference may change i think
+      Object svc = type.cast(tracker.waitForService(timeout));
+      if (svc == null) {
+        throw new RuntimeException("Gave up waiting for service " + flt);
+      }
+      return type.cast(svc);
+    } catch (InvalidSyntaxException e) {
+      throw new IllegalArgumentException("Invalid filter", e);
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+  }
+}