Avoid processing incompatible persistence bundles
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.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 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();
}
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 @@
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 @@
* 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 final Map<Bundle, Collection<PersistenceProviderTracker>> trackers;
- private Map<Integer, String> typeMap;
+
+ 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";
- public PersistenceBundleTracker() {
- trackers = new HashMap<Bundle, Collection<PersistenceProviderTracker>>();
+ 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 final Map<Integer, String> typeMap;
+ private final BundleWiring wiring;
+
+ 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 @@
@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 @@
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()) {