/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2014 Wisdom Framework * %% * Licensed 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. * #L% */ package org.wisdom.framework.jpa; import com.google.common.base.Splitter; import org.apache.felix.ipojo.Factory; import org.apache.felix.ipojo.annotations.*; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.util.tracker.BundleTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wisdom.framework.jpa.model.Persistence; import javax.persistence.spi.PersistenceProvider; import javax.xml.bind.JAXB; import java.net.URL; import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; /** * The entry point of the JPA bridge. * This component tracks bundles and check if they contain a {@code Meta-Persistence} header. If so, is creates a * necessary persistence unit. By default, the tracker check for {@code META-INF/persistence.xml} */ @Component(immediate = true) @Instantiate public class JPAManager { /** * The Meta-Persistence header. */ public static final String META_PERSISTENCE = "Meta-Persistence"; /** * The logger. */ private final static Logger LOGGER = LoggerFactory.getLogger(JPAManager.class); /** * The bundle context, used to register the tracker. */ @Context BundleContext context; /** * The factory used to create Persistence Unit 'instances' */ @Requires(filter = "(factory.name=org.wisdom.framework.jpa.PersistenceUnitComponent)") Factory factory; /** * Set to be sure the weaving hook is registered first. */ @Requires JPATransformer transformer; /** * The tracked bundle. */ BundleTracker<PersistentBundle> bundles; @Validate void start() throws Exception { // Track bundles. bundles = new BundleTracker<PersistentBundle>(context, Bundle.ACTIVE + Bundle.STARTING, null) { /** * A new bundle arrives, check whether or not it contains persistence unit. * @param bundle the bundle * @param event the event * @return the Persistence Bundle object if the bundle contain PU, {@code null} if none (bundle not * tracked) */ @Override public PersistentBundle addingBundle(Bundle bundle, BundleEvent event) { try { // Parse any persistence units, returns null (not tracked) when there is no persistence unit return parse(bundle); } catch (Exception e) { LOGGER.error("While parsing bundle {} for a persistence unit we encountered " + "an unexpected exception {}. This bundle (also the other persistence " + "units in this bundle) will be ignored.", bundle, e.getMessage(), e); //noinspection Contract return null; } } /** * A bundle is leaving. * @param bundle the bundle * @param event the event * @param pu the persistent bundle */ @Override public void removedBundle(Bundle bundle, BundleEvent event, PersistentBundle pu) { pu.destroy(); } }; bundles.open(); } /** * Closes the tracker. */ @Invalidate void stop() { bundles.close(); } /** * Check a bundle for persistence units following the rules in the OSGi * spec. * <p> * A Persistence Bundle is a bundle that specifies the Meta-Persistence * header, see Meta Persistence Header on page 439. This header refers to * one or more Persistence Descriptors in the Persistence Bundle. Commonly, * this is the META-INF/persistence.xml resource. This location is the * standard for non- OSGi environments, however an OSGi bundle can also use * other locations as well as multiple resources. Any entity classes must * originate in the bundle's JAR, it cannot come from a fragment. This * requirement is necessary to simplify enhancing entity classes. * * @param bundle the bundle to be searched * @return a Persistent Bundle or null if it has no matching persistence * units */ PersistentBundle parse(Bundle bundle) throws Exception { LOGGER.debug("Analysing bundle {}", bundle.getBundleId()); String metapersistence = bundle.getHeaders().get(META_PERSISTENCE); if (metapersistence == null || metapersistence.trim().isEmpty()) { // Check default location (except for system bundle) if (bundle.getBundleId() != 0 && bundle.getResource("META-INF/persistence.xml") != null) { // Found at the default location metapersistence = "META-INF/persistence.xml"; } else { return null; } } LOGGER.info("META_PERSISTENCE header found in bundle {} : {}", bundle.getBundleId(), metapersistence); // We can have multiple persistence units. Set<Persistence.PersistenceUnit> set = new HashSet<>(); for (String location : Splitter.on(",").omitEmptyStrings().trimResults().split(metapersistence)) { LOGGER.info("Analysing location {}", location); // Lets remember where we came from Persistence.PersistenceUnit.Properties.Property p = new Persistence.PersistenceUnit.Properties.Property(); p.setName("location"); p.setValue(location); // Try to find the resource for the persistence unit // on the classpath: getResource URL url = bundle.getResource(location); if (url == null) { LOGGER.error("Bundle {} specifies location '{}' in the Meta-Persistence header but no such" + " resource is found in the bundle at that location.", bundle, location); } else { // Parse the XML file. Persistence persistence = JAXB.unmarshal(url, Persistence.class); LOGGER.info("Parsed persistence: {}, unit {}", persistence, persistence.getPersistenceUnit()); for (Persistence.PersistenceUnit pu : persistence.getPersistenceUnit()) { if (pu.getProperties() == null) { pu.setProperties(new Persistence.PersistenceUnit.Properties()); } pu.getProperties().getProperty().add(p); set.add(pu); LOGGER.info("Adding persistence unit {}", pu); } } } // Ignore this bundle if no valid PUs if (set.isEmpty()) { LOGGER.warn("No persistence unit found in bundle {}, despite a META_PERSISTENCE header ({})", bundle.getBundleId(), metapersistence); return null; } return new PersistentBundle(bundle, set, factory); } }