Repository: aries-jpa
Updated Branches:
  refs/heads/master 5bb0454e6 -> 0c5cd130c


Avoid processing incompatible persistence bundles


Project: http://git-wip-us.apache.org/repos/asf/aries-jpa/repo
Commit: http://git-wip-us.apache.org/repos/asf/aries-jpa/commit/0c5cd130
Tree: http://git-wip-us.apache.org/repos/asf/aries-jpa/tree/0c5cd130
Diff: http://git-wip-us.apache.org/repos/asf/aries-jpa/diff/0c5cd130

Branch: refs/heads/master
Commit: 0c5cd130c0733ab02023ebeafc4fbc3c648be271
Parents: 5bb0454
Author: timothyjward <[email protected]>
Authored: Fri Jun 9 13:42:22 2017 +0100
Committer: timothyjward <[email protected]>
Committed: Fri Jun 9 13:42:22 2017 +0100

----------------------------------------------------------------------
 .../aries/jpa/container/impl/Activator.java     |   3 +-
 .../impl/PersistenceBundleTracker.java          | 136 ++++++++++++++++++-
 2 files changed, 133 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aries-jpa/blob/0c5cd130/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/Activator.java
----------------------------------------------------------------------
diff --git 
a/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/Activator.java
 
b/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/Activator.java
index 6f51d85..afa8df0 100644
--- 
a/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/Activator.java
+++ 
b/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/Activator.java
@@ -28,6 +28,7 @@ import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.util.tracker.BundleTracker;
 
 public class Activator implements BundleActivator {
@@ -38,7 +39,7 @@ public class Activator implements BundleActivator {
     public void start(BundleContext context) throws Exception {
         registerWeavingHook(context, TransformerRegistrySingleton.get());
 
-        PersistenceBundleTracker customizer = new PersistenceBundleTracker();
+        PersistenceBundleTracker customizer = new 
PersistenceBundleTracker(context.getBundle().adapt(BundleWiring.class));
         persistenceBundleManager = new BundleTracker<Bundle>(context, 
Bundle.STARTING | Bundle.ACTIVE, customizer);
         persistenceBundleManager.open();
     }

http://git-wip-us.apache.org/repos/asf/aries-jpa/blob/0c5cd130/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java
----------------------------------------------------------------------
diff --git 
a/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java
 
b/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java
index 6981256..da694f8 100644
--- 
a/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java
+++ 
b/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java
@@ -22,12 +22,17 @@ package org.apache.aries.jpa.container.impl;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.aries.jpa.container.parser.impl.PersistenceUnit;
 import org.apache.aries.jpa.container.parser.impl.PersistenceUnitParser;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleEvent;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
 import org.osgi.util.tracker.BundleTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,12 +42,24 @@ import org.slf4j.LoggerFactory;
  * found a PersistenceProviderTracker is installed that tracks matching 
providers.
  */
 public class PersistenceBundleTracker implements 
BundleTrackerCustomizer<Bundle> {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(PersistenceBundleTracker.class);
+       
+       private static final String OSGI_EXTENDER_NS = "osgi.extender";
+       
+       private static final String OSGI_CONTRACT_NS = "osgi.contract";
+       private static final String JAVA_JPA_CONTRACT = "JavaJPA";
+
+       private static final String OSGI_PACKAGE_NS = "osgi.wiring.package";
+       private static final String JAVAX_PERSISTENCE_PKG = "javax.persistence";
+       private static final String JPA_SERVICE_PKG = "org.osgi.service.jpa";
+       
+       private static final Logger LOGGER = 
LoggerFactory.getLogger(PersistenceBundleTracker.class);
     private final Map<Bundle, Collection<PersistenceProviderTracker>> trackers;
-    private Map<Integer, String> typeMap;
+    private final Map<Integer, String> typeMap;
+    private final BundleWiring wiring;
 
-    public PersistenceBundleTracker() {
-        trackers = new HashMap<Bundle, 
Collection<PersistenceProviderTracker>>();
+    public PersistenceBundleTracker(BundleWiring bundleWiring) {
+        wiring = bundleWiring;
+               trackers = new HashMap<Bundle, 
Collection<PersistenceProviderTracker>>();
         this.typeMap = new HashMap<Integer, String>();
         this.typeMap.put(BundleEvent.INSTALLED, "INSTALLED");
         this.typeMap.put(BundleEvent.LAZY_ACTIVATION, "LAZY_ACTIVATION");
@@ -57,6 +74,21 @@ public class PersistenceBundleTracker implements 
BundleTrackerCustomizer<Bundle>
 
     @Override
     public synchronized Bundle addingBundle(Bundle bundle, BundleEvent event) {
+       
+       if(incompatibleExtender(bundle)) {
+               // We must not process bundles that we aren't compatible with
+               LOGGER.info("The bundle {} is wired to a different JPA Extender 
and must be ignored.", 
+                               bundle.getSymbolicName());
+               return null;
+       }
+       
+       if(incompatibleClassSpace(bundle)) {
+               // We must not process bundles that we aren't compatible with
+               LOGGER.warn("The bundle {} does not share a class space with 
the JPA Extender and must be ignored.", 
+                               bundle.getSymbolicName());
+               return null;
+       }
+       
         if (event != null && event.getType() == BundleEvent.STOPPED) {
             // Avoid starting persistence units in state STOPPED.
             // TODO No idea why we are called at all in this state
@@ -68,7 +100,101 @@ public class PersistenceBundleTracker implements 
BundleTrackerCustomizer<Bundle>
         return bundle;
     }
 
-    @Override
+    private boolean incompatibleExtender(Bundle bundle) {
+       
+               List<BundleWire> requiredWires = 
bundle.adapt(BundleWiring.class)
+                               .getRequiredWires(OSGI_EXTENDER_NS);
+               
+               for(BundleWire bw : requiredWires) {
+                       BundleCapability capability = bw.getCapability();
+                       
if(EntityManagerFactoryBuilder.JPA_CAPABILITY_NAME.equals(
+                                       
capability.getAttributes().get(OSGI_EXTENDER_NS))) {
+                               
+                               // If the persistence bundle requires a 
different revision for the 
+                               // JPA extender then we are incompatible, 
otherwise we are
+                               return 
!capability.getRevision().equals(wiring.getRevision());
+                       }
+               }
+               
+               // If there is no requirement then we must assume that it's safe
+               return false;
+       }
+
+    /**
+     * Sufficient Criteria for having/failing class space compatibility - 
+     * <ol>
+     *   <li>Sharing a contract for <code>JavaJPA</code></li>
+     *   <li>Sharing a provider of <code>javax.persistence</code></li>
+     *   <li>Sharing a provider of <code>org.osgi.service.jpa</code></li>
+     * </ol>
+     * 
+     * @param bundle
+     * @return
+     */
+       private boolean incompatibleClassSpace(Bundle bundle) {
+               BundleWiring pbWiring = bundle.adapt(BundleWiring.class);
+               
+               BundleCapability pbContract = getUsedCapability(pbWiring, 
OSGI_CONTRACT_NS, JAVA_JPA_CONTRACT);
+               
+               if(pbContract != null) {
+                       LOGGER.debug("Matching JPA contract for possible 
persistence bundle {}", bundle.getSymbolicName());
+                       
+                       BundleCapability implContract = 
getUsedCapability(pbWiring, OSGI_CONTRACT_NS, JAVA_JPA_CONTRACT);
+                       return !pbContract.equals(implContract);
+               }
+               
+               // No contract required by the persistence bundle, try 
javax.persistence
+               BundleCapability pbJavaxPersistence = 
getUsedCapability(pbWiring, 
+                                       OSGI_PACKAGE_NS, JAVAX_PERSISTENCE_PKG);
+                       
+               if(pbJavaxPersistence != null) {
+                       LOGGER.debug("Matching JPA API package for possible 
persistence bundle {}", bundle.getSymbolicName());
+                       
+                       BundleCapability implJavaxPersistence = 
getUsedCapability(pbWiring, 
+                                       OSGI_PACKAGE_NS, JAVAX_PERSISTENCE_PKG);
+                       return !pbJavaxPersistence.equals(implJavaxPersistence);
+               }
+
+               // No jpa package required by the persistence bundle, try 
org.osgi.service.jpa
+               BundleCapability pbJpaService = getUsedCapability(pbWiring, 
+                               OSGI_PACKAGE_NS, JPA_SERVICE_PKG);
+               
+               if(pbJpaService != null) {
+                       LOGGER.debug("Matching JPA service package for possible 
persistence bundle {}", bundle.getSymbolicName());
+                       
+                       BundleCapability implJpaService = 
getUsedCapability(pbWiring, 
+                                       OSGI_PACKAGE_NS, JPA_SERVICE_PKG);
+                       return !pbJpaService.equals(implJpaService);
+               }
+               
+               // If there is nothing to clash on then we must assume that 
it's safe
+               return false;
+       }
+
+       private BundleCapability getUsedCapability(BundleWiring toCheck, String 
ns, String attr) {
+               BundleCapability cap = null;
+               
+               for(BundleWire bw : toCheck.getRequiredWires(ns)) {
+                       BundleCapability capability = bw.getCapability();
+                       if(attr.equals(capability.getAttributes().get(ns))) {
+                               cap = capability;
+                               break;
+                       }
+               }
+               
+               if(cap == null) {
+                       for(BundleCapability capability : 
toCheck.getCapabilities(ns)) {
+                               
if(attr.equals(capability.getAttributes().get(ns))) {
+                                       cap = capability;
+                                       break;
+                               }
+                       }
+               }
+               
+               return cap;
+       }
+
+       @Override
     public synchronized void removedBundle(Bundle bundle, BundleEvent event, 
Bundle object) {
         Collection<PersistenceProviderTracker> providerTrackers = 
trackers.remove(bundle);
         if (providerTrackers == null || providerTrackers.isEmpty()) {

Reply via email to