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()) {
