Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DSFTracker.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DSFTracker.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DSFTracker.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DSFTracker.java Tue May 19 09:47:49 2015 @@ -0,0 +1,125 @@ +/* + * 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.impl; + +import java.sql.SQLException; +import java.util.Properties; + +import javax.persistence.spi.PersistenceProvider; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; + +import org.apache.aries.jpa.container.parser.impl.PersistenceUnit; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.jdbc.DataSourceFactory; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DSFTracker extends ServiceTracker<DataSourceFactory, ManagedEMF>{ + private static final String JDBC_DRIVER = "javax.persistence.jdbc.driver"; + private static final String JDBC_URL = "javax.persistence.jdbc.url"; + private static final String JDBC_USER = "javax.persistence.jdbc.user"; + private static final String JDBC_PASSWORD = "javax.persistence.jdbc.password"; + + private static final Logger LOGGER = LoggerFactory.getLogger(DSFTracker.class); + + + private PersistenceProvider provider; + private PersistenceUnit punit; + + public DSFTracker(BundleContext context, PersistenceProvider provider, PersistenceUnit punit) { + super(context, createFilter(context, punit), null); + this.provider = provider; + this.punit = punit; + } + + static Filter createFilter(BundleContext context, PersistenceUnit punit) { + String driverName = getDriverName(punit); + if (driverName == null) { + throw new IllegalArgumentException("No javax.persistence.jdbc.driver supplied in persistence.xml"); + } + String filter = String.format("(&(objectClass=%s)(%s=%s))", + DataSourceFactory.class.getName(), + DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, + driverName); + LOGGER.info("Tracking DataSourceFactory for punit " + punit.getPersistenceUnitName() + " with filter " + filter); + try { + return context.createFilter(filter); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + public static String getDriverName(PersistenceUnit punit) { + return (String)punit.getProperties().get(JDBC_DRIVER); + } + + @Override + public ManagedEMF addingService(ServiceReference<DataSourceFactory> reference) { + LOGGER.info("Found DataSourceFactory for " + punit.getPersistenceUnitName() + " " + + getDriverName(punit)); + try { + DataSourceFactory dsf = context.getService(reference); + DataSource ds = createDataSource(dsf); + if (punit.getTransactionType() == PersistenceUnitTransactionType.JTA) { + punit.setJtaDataSource(ds); + } else { + punit.setNonJtaDataSource(ds); + } + BundleContext containerContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); + return new ManagedEMF(containerContext, punit.getBundle(), provider, punit); + } catch (Exception e) { + LOGGER.error("Error creating DataSource for punit " + punit.getPersistenceUnitName(), e); + return null; + } + } + + private DataSource createDataSource(DataSourceFactory dsf) { + try { + Properties props = new Properties(); + put(props, DataSourceFactory.JDBC_URL, punit, JDBC_URL); + put(props, DataSourceFactory.JDBC_USER, punit, JDBC_USER); + put(props, DataSourceFactory.JDBC_PASSWORD, punit, JDBC_PASSWORD); + DataSource ds = dsf.createDataSource(props); + return ds; + } catch (SQLException e) { + throw new RuntimeException("Error creating DataSource for persistence unit " + punit + "." + + e.getMessage(), e); + } + } + + private static void put(Properties props, String destKey, PersistenceUnit punit, String sourceKey) { + Object value = punit.getProperties().get(sourceKey); + if (value != null) { + props.put(destKey, value); + } + } + + @Override + public void removedService(ServiceReference<DataSourceFactory> reference, ManagedEMF managedEMF) { + LOGGER.info("Lost DataSourceFactory for " + punit.getPersistenceUnitName() + " " + getDriverName(punit)); + managedEMF.close(); + super.removedService(reference, managedEMF); + } +}
Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DataSourceTracker.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DataSourceTracker.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DataSourceTracker.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/DataSourceTracker.java Tue May 19 09:47:49 2015 @@ -0,0 +1,103 @@ +/* + * 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.impl; + +import static javax.persistence.spi.PersistenceUnitTransactionType.JTA; + +import javax.persistence.spi.PersistenceProvider; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; + +import org.apache.aries.jpa.container.parser.impl.PersistenceUnit; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DataSourceTracker extends ServiceTracker<DataSource, ManagedEMF>{ + private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceTracker.class); + + private PersistenceProvider provider; + private PersistenceUnit punit; + + static final String DS_PREFIX = "osgi:service/javax.sql.DataSource"; + + public DataSourceTracker(BundleContext context, PersistenceProvider provider, PersistenceUnit punit) { + super(context, createFilter(context, punit), null); + this.provider = provider; + this.punit = punit; + } + + static Filter createFilter(BundleContext context, PersistenceUnit punit) { + String dsName = getDsName(punit); + if (dsName == null) { + throw new IllegalArgumentException("No DataSource supplied in persistence.xml"); + } + String subFilter = getSubFilter(dsName); + String filter = String.format("(&(objectClass=%s)%s)", + DataSource.class.getName(), + subFilter); + LOGGER.info("Tracking DataSource for punit " + punit.getPersistenceUnitName() + " with filter " + filter); + try { + return context.createFilter(filter); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + private static String getSubFilter(String dsName) { + if (dsName.startsWith(DS_PREFIX)) { + return (dsName.length() > DS_PREFIX.length() +1) + ? dsName.substring(DS_PREFIX.length()+1) + : "(osgi.jndi.service.name=*)"; + } else { + return "(osgi.jndi.service.name=" + dsName + ")"; + } + } + + private static String getDsName(PersistenceUnit punit) { + return punit.getTransactionType() == JTA ? punit.getJtaDataSourceName() : punit.getNonJtaDataSourceName(); + } + + @Override + public ManagedEMF addingService(ServiceReference<DataSource> reference) { + LOGGER.info("Found DataSource for " + punit.getPersistenceUnitName() + " " + getDsName(punit)); + DataSource ds = context.getService(reference); + if (punit.getTransactionType() == PersistenceUnitTransactionType.JTA) { + punit.setJtaDataSource(ds); + } else { + punit.setNonJtaDataSource(ds); + } + BundleContext containerContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); + return new ManagedEMF(containerContext, punit.getBundle(), provider, punit); + } + + + @Override + public void removedService(ServiceReference<DataSource> reference, ManagedEMF managedEMF) { + LOGGER.info("Lost DataSource for " + punit.getPersistenceUnitName() + " " + getDsName(punit)); + managedEMF.close(); + super.removedService(reference, managedEMF); + } + +} Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/ManagedEMF.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/ManagedEMF.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/ManagedEMF.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/ManagedEMF.java Tue May 19 09:47:49 2015 @@ -0,0 +1,133 @@ +/* + * 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.impl; + +import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_NAME; +import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_PROVIDER; +import static org.osgi.service.jpa.EntityManagerFactoryBuilder.JPA_UNIT_VERSION; + +import java.io.Closeable; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.spi.PersistenceProvider; +import javax.persistence.spi.PersistenceUnitInfo; +import javax.persistence.spi.PersistenceUnitTransactionType; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.cm.ConfigurationException; +import org.osgi.service.cm.ManagedService; +import org.osgi.service.jpa.EntityManagerFactoryBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Creates an EntityManagerFactory(EMF) for a persistence unit and publishes it as a service. + * Custom properties can be configured by supplying a config admin configuriation named like + * the JPA_CONFIGURATION_PREFIX.<persistence unit name>. + */ +public class ManagedEMF implements Closeable, ManagedService { + private static final Logger LOGGER = LoggerFactory.getLogger(ManagedEMF.class); + + private static String JPA_CONFIGURATION_PREFIX = "org.apache.aries.jpa."; + + private EntityManagerFactory emf; + private ServiceRegistration<EntityManagerFactory> reg; + private ServiceRegistration<EntityManagerFactoryBuilder> regBuilder; + private PersistenceProvider provider; + private PersistenceUnitInfo persistenceUnit; + private Bundle bundle; + + public ManagedEMF(BundleContext containerContext, Bundle bundle, PersistenceProvider provider, PersistenceUnitInfo persistenceUnit) { + this.provider = provider; + this.persistenceUnit = persistenceUnit; + this.bundle = bundle; + + Dictionary<String, Object> configuration = new Hashtable<String, Object>(); + configuration.put(Constants.SERVICE_PID, + JPA_CONFIGURATION_PREFIX + persistenceUnit.getPersistenceUnitName()); + containerContext.registerService(ManagedService.class.getName(), this, configuration); + } + + public void close() { + try { + reg.unregister(); + } catch (Exception e) { + // Ignore. May happen if persistence unit bundle is unloaded/updated + } + try { + regBuilder.unregister(); + } catch (Exception e) { + // Ignore. May happen if persistence unit bundle is unloaded/updated + } + if (emf != null) { + try { + emf.close(); + } catch (Exception e) { + LOGGER.warn("EntityManagerFactory for " + persistenceUnit.getPersistenceUnitName() + " already close", e); + } + } + reg = null; + emf = null; + } + + @Override + public void updated(Dictionary<String, ?> properties) throws ConfigurationException { + if (emf != null) { + close(); + } + Map<String, Object> overrides = (properties != null) ? asMap(properties) : null; + LOGGER.info("Registering EntityManagerFactory for persistence unit " + persistenceUnit.getPersistenceUnitName()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Using properties override " + overrides); + } + emf = provider.createContainerEntityManagerFactory(persistenceUnit, overrides); + Dictionary<String, String> props = createProperties(persistenceUnit, bundle); + BundleContext uctx = bundle.getBundleContext(); + reg = uctx.registerService(EntityManagerFactory.class, emf, props); + } + + public static Dictionary<String, String> createProperties(PersistenceUnitInfo persistenceUnit, Bundle puBundle) { + Dictionary<String, String> props = new Hashtable<>(); + props.put(JPA_UNIT_NAME, persistenceUnit.getPersistenceUnitName()); + if (persistenceUnit.getPersistenceProviderClassName() != null) { + props.put(JPA_UNIT_PROVIDER, persistenceUnit.getPersistenceProviderClassName()); + } + props.put(JPA_UNIT_VERSION, puBundle.getVersion().toString()); + return props; + } + + private Map<String, Object> asMap(Dictionary<String, ?> dict) { + Map<String, Object> map = new HashMap<String, Object>(); + map.put(PersistenceUnitTransactionType.class.getName(), persistenceUnit.getTransactionType()); + for (Enumeration<String> e = dict.keys(); e.hasMoreElements();) { + String key = e.nextElement(); + map.put(key, dict.get(key)); + } + return map; + } + +} Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceBundleTracker.java Tue May 19 09:47:49 2015 @@ -0,0 +1,136 @@ +/* + * 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.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +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.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.util.tracker.BundleTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Looks for bundles containing a persistence.xml. For each persistence unit + * found a PersistenceProviderTracker is installed that tracks matching providers. + */ +@SuppressWarnings("deprecation") +public class PersistenceBundleTracker implements BundleTrackerCustomizer<Bundle> { + private static final Logger LOGGER = LoggerFactory.getLogger(PersistenceBundleTracker.class); + Map<Bundle, Collection<PersistenceProviderTracker>> trackers; + private BundleContext context; + + public PersistenceBundleTracker(BundleContext context) { + this.context = context; + trackers = new HashMap<Bundle, Collection<PersistenceProviderTracker>>(); + } + + public Bundle addingBundle(Bundle bundle, BundleEvent event) { + if (getTrackers(bundle).size() == 0) { + findPersistenceUnits(bundle); + } + return bundle; + } + + public void removedBundle(Bundle bundle, BundleEvent event, Bundle object) { + Collection<PersistenceProviderTracker> providerTrackers = trackers.get(bundle); + if (providerTrackers != null) { + if (providerTrackers.size() > 0) { + LOGGER.info("removing persistence units for " + bundle.getSymbolicName() + " " + getType(event)); + } + for (PersistenceProviderTracker providerTracker : providerTrackers) { + providerTracker.close(); + } + providerTrackers.clear(); + trackers.remove(bundle); + } + } + + private void findPersistenceUnits(Bundle bundle) { + ServiceReference<PackageAdmin> ref = context.getServiceReference(PackageAdmin.class); + PackageAdmin packageAdmin = context.getService(ref); + for (PersistenceUnit punit : PersistenceUnitParser.getPersistenceUnits(bundle)) { + punit.addAnnotated(packageAdmin); + trackProvider(bundle, punit); + } + context.ungetService(ref); + } + + private void trackProvider(Bundle bundle, + PersistenceUnit punit) { + LOGGER.info(String.format("Found persistence unit %s in bundle %s with provider %s.", + punit.getPersistenceUnitName(), bundle.getSymbolicName(), + punit.getPersistenceProviderClassName())); + PersistenceProviderTracker tracker = new PersistenceProviderTracker(context, punit); + tracker.open(); + getTrackers(bundle).add(tracker); + } + + @Override + public void modifiedBundle(Bundle bundle, BundleEvent event, Bundle object) { + } + + private static String getType(BundleEvent event) { + if (event == null) { + return "null"; + } + int type = event.getType(); + switch (type) { + case BundleEvent.INSTALLED: + return "INSTALLED"; + case BundleEvent.LAZY_ACTIVATION: + return "LAZY_ACTIVATION"; + case BundleEvent.RESOLVED: + return "RESOLVED"; + case BundleEvent.STARTED: + return "STARTED"; + case BundleEvent.STARTING: + return "Starting"; + case BundleEvent.STOPPED: + return "STOPPED"; + case BundleEvent.UNINSTALLED: + return "UNINSTALLED"; + case BundleEvent.UNRESOLVED: + return "UNRESOLVED"; + case BundleEvent.UPDATED: + return "UPDATED"; + default: + return "unknown event type: " + type; + } + } + + private Collection<PersistenceProviderTracker> getTrackers(Bundle bundle) { + Collection<PersistenceProviderTracker> providerTrackers = trackers.get(bundle); + if (providerTrackers == null) { + providerTrackers = new ArrayList<>(); + trackers.put(bundle, providerTrackers); + } + return providerTrackers; + } + +} Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceProviderTracker.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceProviderTracker.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceProviderTracker.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/PersistenceProviderTracker.java Tue May 19 09:47:49 2015 @@ -0,0 +1,156 @@ +/* + * 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.impl; + +import java.util.Dictionary; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.spi.PersistenceProvider; +import javax.sql.DataSource; + +import org.apache.aries.jpa.container.parser.impl.PersistenceUnit; +import org.apache.aries.jpa.container.weaving.impl.DummyDataSource; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.jpa.EntityManagerFactoryBuilder; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tracks matching persistence providers for a persistence unit. + * If a provider is found: + * - an EntityManagerFactoryBuilder is installed + * - A DataSourceTracker is installed if the JtaDataSource refers to an OSGi service + */ +public class PersistenceProviderTracker extends ServiceTracker<PersistenceProvider, StoredPerProvider> { + private static final String JAVAX_PERSISTENCE_PROVIDER = "javax.persistence.provider"; + + private static final Logger LOGGER = LoggerFactory.getLogger(PersistenceProviderTracker.class); + + private PersistenceUnit punit; + + public PersistenceProviderTracker(BundleContext context, PersistenceUnit punit) { + super(context, createFilter(context, punit), null); + this.punit = punit; + } + + private static Filter createFilter(BundleContext context, PersistenceUnit punit) { + String filter = null; + if (punit.getPersistenceProviderClassName() != null) { + filter = String.format("(&(objectClass=%s)(%s=%s))", + PersistenceProvider.class.getName(), + JAVAX_PERSISTENCE_PROVIDER, + punit.getPersistenceProviderClassName()); + } else { + filter = String.format("(objectClass=%s)", PersistenceProvider.class.getName()); + } + + try { + return context.createFilter(filter); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public StoredPerProvider addingService(ServiceReference<PersistenceProvider> reference) { + String providerName = (String)reference.getProperty(JAVAX_PERSISTENCE_PROVIDER); + // FIXME should be set when creating the EMF was successful + if (punit.getPersistenceProviderClassName() == null) { + punit.setProviderClassName(providerName); + } + StoredPerProvider stored = new StoredPerProvider(); + LOGGER.info("Found provider for " + punit.getPersistenceUnitName() + " " + punit.getPersistenceProviderClassName()); + PersistenceProvider provider = context.getService(reference); + + createAndCloseDummyEMF(provider); + + stored.dsTracker = createDataSourceTracker(stored, provider, providerName); + EntityManagerFactoryBuilder emfBuilder = new AriesEntityManagerFactoryBuilder(provider, punit); + Dictionary<String, ?> props = ManagedEMF.createProperties(punit, punit.getBundle()); + stored.reg = context.registerService(EntityManagerFactoryBuilder.class, emfBuilder , props); + return stored; + } + + /** + * Create and close a dummy EMF to give the PersistenceProvider a chance to call + * punit.addTransformer(). This has to occur as early as possible as weaving needs + * to be done before the first entity class is loaded. So we can not wait till the + * real DataSource is found. + */ + private void createAndCloseDummyEMF(PersistenceProvider provider) { + DataSource dummyDataSource = new DummyDataSource(); + punit.setJtaDataSource(dummyDataSource); + punit.setNonJtaDataSource(dummyDataSource); + try { + EntityManagerFactory emf = provider.createContainerEntityManagerFactory(punit, null); + emf.close(); + } catch (Exception e) { + LOGGER.warn(e.getMessage(), e); + } + punit.setJtaDataSource(null); + punit.setNonJtaDataSource(null); + } + + private ServiceTracker<?, ?> createDataSourceTracker(StoredPerProvider stored, PersistenceProvider provider, String providerName) { + if (usesDataSource()) { + if (!usesDataSourceService()) { + LOGGER.warn("Persistence unit " + punit.getPersistenceUnitName() + " refers to a non OSGi service DataSource"); + return null; + } + DataSourceTracker dsTracker = new DataSourceTracker(context, provider, punit); + dsTracker.open(); + return dsTracker; + } else if (usesDSF()) { + DSFTracker dsfTracker = new DSFTracker(context, provider, punit); + dsfTracker.open(); + return dsfTracker; + } else { + LOGGER.debug("Persistence unit " + punit.getPersistenceUnitName() + " does not refer a DataSource. " + +"It can only be used with EntityManagerFactoryBuilder."); + return null; + } + } + + private boolean usesDataSource() { + return punit.getJtaDataSourceName() != null || punit.getNonJtaDataSourceName() != null; + } + + private boolean usesDSF() { + return DSFTracker.getDriverName(punit) != null; + } + + private boolean usesDataSourceService() { + return punit.getJtaDataSourceName() != null && punit.getJtaDataSourceName().startsWith(DataSourceTracker.DS_PREFIX) + || punit.getNonJtaDataSourceName() != null && punit.getNonJtaDataSourceName().startsWith(DataSourceTracker.DS_PREFIX); + } + + @Override + public void removedService(ServiceReference<PersistenceProvider> reference, StoredPerProvider stored) { + LOGGER.info("Lost provider for " + punit.getPersistenceUnitName() + " " + punit.getPersistenceProviderClassName()); + if (stored.dsTracker != null) { + stored.dsTracker.close(); + } + stored.reg.unregister(); + super.removedService(reference, stored); + } +} Copied: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/StoredPerProvider.java (from r1680054, aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/InvalidPersistenceUnitException.java) URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/StoredPerProvider.java?p2=aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/StoredPerProvider.java&p1=aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/InvalidPersistenceUnitException.java&r1=1680054&r2=1680218&rev=1680218&view=diff ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/InvalidPersistenceUnitException.java (original) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/impl/StoredPerProvider.java Tue May 19 09:47:49 2015 @@ -18,22 +18,11 @@ */ package org.apache.aries.jpa.container.impl; -/** - * This exception is thrown if an {@link EntityManagerFactoryManager} has - * entered an invalid state and needs to be destroyed - */ -public class InvalidPersistenceUnitException extends Exception { - - /** - * For serialization - */ - private static final long serialVersionUID = 6523462131213055375L; - - public InvalidPersistenceUnitException(Exception e) { - super(e); - } - - public InvalidPersistenceUnitException() { - } +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.jpa.EntityManagerFactoryBuilder; +import org.osgi.util.tracker.ServiceTracker; -} +public class StoredPerProvider { + ServiceTracker<?, ?> dsTracker; + ServiceRegistration<EntityManagerFactoryBuilder> reg; +} \ No newline at end of file Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScanner.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScanner.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScanner.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScanner.java Tue May 19 09:47:49 2015 @@ -0,0 +1,59 @@ +/* + * 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.parser.impl; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.MappedSuperclass; + +import org.osgi.framework.Bundle; +import org.osgi.framework.wiring.BundleWiring; + +@SuppressWarnings("deprecation") +class JPAAnnotationScanner { + public static Collection<String> findJPAAnnotatedClasses(Bundle b) { + BundleWiring bw = b.adapt(BundleWiring.class); + Collection<String> resources = bw.listResources("/", "*.class", + BundleWiring.LISTRESOURCES_LOCAL | BundleWiring.LISTRESOURCES_RECURSE); + + Collection<String> classes = new ArrayList<String>(); + ClassLoader cl = new TempBundleDelegatingClassLoader(b, JPAAnnotationScanner.class.getClassLoader()); + for(String s : resources) { + s = s.replace('/', '.').substring(0, s.length() - 6); + try { + Class<?> clazz = Class.forName(s, false, cl); + + if(clazz.isAnnotationPresent(Entity.class) || + clazz.isAnnotationPresent(MappedSuperclass.class) || + clazz.isAnnotationPresent(Embeddable.class)) { + classes.add(s); + } + + } catch (ClassNotFoundException cnfe) { + + } catch (NoClassDefFoundError ncdfe) { + + } + } + return classes; + } +} Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScannerXbean.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScannerXbean.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScannerXbean.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAAnnotationScannerXbean.java Tue May 19 09:47:49 2015 @@ -0,0 +1,60 @@ +/* + * 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.parser.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.MappedSuperclass; + +import org.apache.xbean.finder.Annotated; +import org.apache.xbean.finder.BundleAnnotationFinder; +import org.osgi.framework.Bundle; +import org.osgi.service.packageadmin.PackageAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("deprecation") +class JPAAnnotationScannerXbean { + private static Logger LOGGER = LoggerFactory.getLogger(JPAAnnotationScannerXbean.class); + + public static Collection<String> findJPAAnnotatedClasses(Bundle bundle, PackageAdmin packageAdmin) { + Collection<String> classes = new ArrayList<String>(); + try { + BundleAnnotationFinder finder = new BundleAnnotationFinder(packageAdmin, bundle); + Set<Annotated<Class<?>>> annotatedSet = new HashSet<Annotated<Class<?>>>(); + annotatedSet.addAll(finder.findMetaAnnotatedClasses(Entity.class)); + annotatedSet.addAll(finder.findMetaAnnotatedClasses(MappedSuperclass.class)); + annotatedSet.addAll(finder.findMetaAnnotatedClasses(Embeddable.class)); + LOGGER.info("Searching for entities"); + for (Annotated<Class<?>> annotated : annotatedSet) { + LOGGER.info("Found entity " + annotated.get().getName()); + classes.add(annotated.get().getName()); + } + } catch (Exception e) { + e.printStackTrace(); + } + return classes; + } + +} \ No newline at end of file Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAHandler.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAHandler.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAHandler.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/JPAHandler.java Tue May 19 09:47:49 2015 @@ -0,0 +1,127 @@ +/* + * 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.parser.impl; + +import java.util.Collection; +import java.util.Stack; + +import javax.persistence.SharedCacheMode; +import javax.persistence.ValidationMode; +import javax.persistence.spi.PersistenceUnitTransactionType; + +import org.osgi.framework.Bundle; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * This code is responsible for parsing the persistence.xml into PersistenceUnits + */ +public class JPAHandler extends DefaultHandler { + /** The Persistence Units that we have parsed */ + private final Stack<PersistenceUnit> persistenceUnits = new Stack<PersistenceUnit>(); + /** The name of the current element */ + private String elementName; + /** The version of the persistence.xml file */ + /** A StringBuilder for caching the information from getCharacters */ + private StringBuilder builder = new StringBuilder(); + /** The bundle that contains this persistence descriptor */ + private Bundle bundle; + + /** + * Create a new JPA Handler for a bundle + * + * @param bundle + */ + public JPAHandler(Bundle bundle) { + this.bundle = bundle; + } + + /** + * Collect up the characters, as element's characters may be split across multiple calls. Isn't SAX + * lovely... + */ + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + builder.append(ch, start, length); + } + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) + throws SAXException { + // Do this setting first as we use it later. + elementName = (localName == null || "".equals(localName)) ? name : localName; + + if ("persistence-unit".equals(elementName)) { + String tranTypeSt = attributes.getValue("transaction-type"); + PersistenceUnitTransactionType tranType = tranTypeSt == null ? null : PersistenceUnitTransactionType.valueOf(tranTypeSt); + persistenceUnits.push(new PersistenceUnit(bundle, + attributes.getValue("name"), + tranType)); + } else if ("exclude-unlisted-classes".equals(elementName)) + persistenceUnits.peek().setExcludeUnlisted(true); + else if ("property".equals(elementName)) + persistenceUnits.peek().addProperty(attributes.getValue("name"), attributes.getValue("value")); + + } + + @Override + public void endElement(String uri, String localName, String name) throws SAXException { + String s = builder.toString().trim(); + // This step is VERY important, otherwise we pollute subsequent + // elements + builder = new StringBuilder(); + + if ("".equals(s)) + return; + + PersistenceUnit pu = persistenceUnits.peek(); + + if ("provider".equals(elementName)) + pu.setProviderClassName(s); + else if ("jta-data-source".equals(elementName)) + pu.setJtaDataSourceName(s); + else if ("non-jta-data-source".equals(elementName)) + pu.setNonJtaDataSourceName(s); + else if ("class".equals(elementName)) + pu.addClassName(s); + else if ("exclude-unlisted-classes".equals(elementName)) + pu.setExcludeUnlisted(Boolean.parseBoolean(s)); + else if ("shared-cache-mode".equals(elementName)) + pu.setSharedCacheMode(SharedCacheMode.valueOf(s)); + else if ("validation-mode".equals(elementName)) + pu.setValidationMode(ValidationMode.valueOf(s)); + } + + @Override + public void error(SAXParseException spe) throws SAXException { + // We throw this exception to be caught further up and logged + // as an error there + throw spe; + } + + /** + * @return The collection of persistence units that we have parsed + */ + public Collection<PersistenceUnit> getPersistenceUnits() { + return persistenceUnits; + } + +} Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnit.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnit.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnit.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnit.java Tue May 19 09:47:49 2015 @@ -0,0 +1,229 @@ +/* + * 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.parser.impl; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import javax.persistence.SharedCacheMode; +import javax.persistence.ValidationMode; +import javax.persistence.spi.ClassTransformer; +import javax.persistence.spi.PersistenceUnitInfo; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; + +import org.apache.aries.jpa.container.weaving.impl.TransformerRegistry; +import org.apache.aries.jpa.container.weaving.impl.TransformerRegistrySingleton; +import org.osgi.framework.Bundle; +import org.osgi.framework.wiring.BundleWiring; +import org.osgi.service.packageadmin.PackageAdmin; + +public class PersistenceUnit implements PersistenceUnitInfo { + + private Bundle bundle; + private ClassLoader classLoader; + private Set<String> classNames; + private boolean excludeUnlisted; + private DataSource jtaDataSource; + private String jtaDataSourceName; + private DataSource nonJtaDataSource; + private String nonJtaDataSourceName; + private String persistenceProviderClassName; + private String persistenceUnitName; + private String persistenceXMLSchemaVersion; + private Properties props; + private SharedCacheMode sharedCacheMode = SharedCacheMode.UNSPECIFIED; + private PersistenceUnitTransactionType transactionType; + private ValidationMode validationMode = ValidationMode.NONE; + + public PersistenceUnit(Bundle bundle, String persistenceUnitName, + PersistenceUnitTransactionType transactionType) { + this.bundle = bundle; + this.persistenceUnitName = persistenceUnitName; + this.transactionType = transactionType; + this.props = new Properties(); + this.classLoader = bundle.adapt(BundleWiring.class).getClassLoader(); + this.classNames = new HashSet<>(); + } + + public void addClassName(String className) { + this.classNames.add(className); + } + + public void addProperty(String name, String value) { + props.put(name, value); + } + + @Override + public void addTransformer(ClassTransformer transformer) { + TransformerRegistry reg = TransformerRegistrySingleton.get(); + reg.addTransformer(bundle, transformer); + } + + @Override + public boolean excludeUnlistedClasses() { + return this.excludeUnlisted; + } + + public Bundle getBundle() { + return bundle; + } + + @Override + public ClassLoader getClassLoader() { + return this.classLoader; + } + + @SuppressWarnings("unchecked") + @Override + public List<URL> getJarFileUrls() { + return Collections.EMPTY_LIST; + } + + @Override + public DataSource getJtaDataSource() { + return this.jtaDataSource; + } + + public String getJtaDataSourceName() { + return jtaDataSourceName; + } + + @Override + public List<String> getManagedClassNames() { + ArrayList<String> names = new ArrayList<>(); + names.addAll(classNames); + return names; + } + + @SuppressWarnings("unchecked") + @Override + public List<String> getMappingFileNames() { + return Collections.EMPTY_LIST; + } + + public String getName() { + return persistenceUnitName; + } + + @Override + public ClassLoader getNewTempClassLoader() { + return new TempBundleDelegatingClassLoader(bundle, classLoader); + } + + @Override + public DataSource getNonJtaDataSource() { + return this.nonJtaDataSource; + } + + public String getNonJtaDataSourceName() { + return nonJtaDataSourceName; + } + + @Override + public String getPersistenceProviderClassName() { + return this.persistenceProviderClassName; + } + + @Override + public String getPersistenceUnitName() { + return this.persistenceUnitName; + } + + @Override + public URL getPersistenceUnitRootUrl() { + return bundle.getResource("/"); + } + + @Override + public String getPersistenceXMLSchemaVersion() { + return this.persistenceXMLSchemaVersion; + } + + @Override + public Properties getProperties() { + return this.props; + } + + @Override + public SharedCacheMode getSharedCacheMode() { + return this.sharedCacheMode; + } + + @Override + public PersistenceUnitTransactionType getTransactionType() { + return transactionType; + } + + @Override + public ValidationMode getValidationMode() { + return this.validationMode; + } + + public boolean isExcludeUnlisted() { + return excludeUnlisted; + } + + public void setExcludeUnlisted(boolean excludeUnlisted) { + this.excludeUnlisted = excludeUnlisted; + } + + public void setJtaDataSource(DataSource jtaDataSource) { + this.jtaDataSource = jtaDataSource; + } + + public void setJtaDataSourceName(String jtaDataSourceName) { + this.jtaDataSourceName = jtaDataSourceName; + } + + public void setNonJtaDataSource(DataSource nonJtaDataSource) { + this.nonJtaDataSource = nonJtaDataSource; + } + + public void setNonJtaDataSourceName(String nonJtaDataSourceName) { + this.nonJtaDataSourceName = nonJtaDataSourceName; + } + + public void setProviderClassName(String providerClassName) { + this.persistenceProviderClassName = providerClassName; + } + + public void setSharedCacheMode(SharedCacheMode sharedCacheMode) { + this.sharedCacheMode = sharedCacheMode; + } + + public void setValidationMode(ValidationMode validationMode) { + this.validationMode = validationMode; + } + + public void addAnnotated(PackageAdmin packageAdmin) { + if (!excludeUnlistedClasses()) { + Collection<String> detected = JPAAnnotationScanner.findJPAAnnotatedClasses(bundle); + for (String name : detected) { + addClassName(name); + } + } + } +} Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnitParser.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnitParser.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnitParser.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/PersistenceUnitParser.java Tue May 19 09:47:49 2015 @@ -0,0 +1,187 @@ +/* + * 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.parser.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This helper can be used to locate persistence.xml files in a bundle + */ +public class PersistenceUnitParser { + private static final String DEFAULT_PERSISTENCE_LOCATION = "META-INF/persistence.xml"; + private static final Logger _logger = LoggerFactory.getLogger("org.apache.aries.jpa.container"); + public static final String PERSISTENCE_UNIT_HEADER = "Meta-Persistence"; + + /** + * This method locates persistence descriptor files based on a combination of the default location + * "META-INF/persistence.xml" and the Meta-Persistence header. Note that getEntry is used to ensure we do + * not alter the state of the bundle Note also that web application bundles will never return persistence + * descriptors + * @param context + * + * @param bundle The bundle to search + * @param packageAdmin + * @return + */ + public static Collection<PersistenceUnit> getPersistenceUnits(Bundle bundle) { + Collection<PersistenceUnit> punits = new ArrayList<PersistenceUnit>(); + Dictionary<String, String> headers = bundle.getHeaders(); + String metaPersistence = headers.get(PERSISTENCE_UNIT_HEADER); + + Set<String> locations = new HashSet<String>(); + if (metaPersistence == null) { + return punits; + } + + if (!metaPersistence.isEmpty()) { + // Split apart the header to get the individual entries + for (String s : metaPersistence.split(",")) { + locations.add(s.trim()); + } + } + + if (!locations.contains(DEFAULT_PERSISTENCE_LOCATION)) { + locations.add(DEFAULT_PERSISTENCE_LOCATION); + } + + // Find the file and add it to our list + for (String location : locations) { + try { + InputStream is = locateFile(bundle, location); + if (is != null) { + parse(bundle, location, is, punits); + } + } catch (Exception e) { + _logger.error("exception.while.locating.descriptor", e); + return Collections.emptySet(); + } + } + + return punits; + } + + private static void parse(Bundle bundle, String location, InputStream is, Collection<PersistenceUnit> punits) { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + try { + SAXParser parser = parserFactory.newSAXParser(); + JPAHandler handler = new JPAHandler(bundle); + parser.parse(is, handler); + punits.addAll(handler.getPersistenceUnits()); +// for (PersistenceUnit punit : punits) { + //validate(punit); +// } + } catch (Exception e) { + throw new RuntimeException("persistence.description.parse.error", e); + } finally { + safeClose(is); + } + } + + private static void validate(PersistenceUnit punit) { + if (punit.getTransactionType() == null) { + throw new IllegalArgumentException("No transaction type specified for persistence unit " + punit.getName()); + } + if (punit.getTransactionType() == PersistenceUnitTransactionType.JTA) { + if (punit.getJtaDataSourceName() == null) { + throw new IllegalArgumentException("Must specify jta-data-source for persistence unit " + punit.getName()); + } + } + if (punit.getTransactionType() == PersistenceUnitTransactionType.RESOURCE_LOCAL) { + if (punit.getNonJtaDataSourceName() == null) { + throw new IllegalArgumentException("Must specify non-jta-data-source for persistence unit " + punit.getName()); + } + } + } + + private static void safeClose(InputStream is) { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // No logging necessary, just consume + } + } + } + + /** + * Locate a persistence descriptor file in a bundle based on a String name. + * + * @param bundle + * @param persistenceXmlFiles + * @param jarLocation + * @throws IOException + */ + private static InputStream locateFile(Bundle bundle, String location) throws IOException { + // There is nothing for an empty location + InputStream is = null; + if ("".equals(location)) { + return null; + } + + // If there is a '!' then we have to look in a jar + int bangIndex = location.indexOf('!'); + // No '!', getEntry will do + if (bangIndex == -1) { + URL url = bundle.getEntry(location); + + if (url != null) + is = url.openStream(); + + } else { + // There was a '!', find the jar + URL url = bundle.getEntry(location.substring(0, bangIndex)); + + if (url != null) { + // Remember to trim off the "!/" + String toLocate = location.substring(bangIndex + 2); + + @SuppressWarnings("resource") + JarInputStream jis = new JarInputStream(url.openStream()); + JarEntry entry = jis.getNextJarEntry(); + + while (entry != null) { + if (entry.getName().equals(toLocate)) { + is = jis; + break; + } + entry = jis.getNextJarEntry(); + } + } + } + return is; + } +} Copied: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/TempBundleDelegatingClassLoader.java (from r1680054, aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/unit/impl/TempBundleDelegatingClassLoader.java) URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/TempBundleDelegatingClassLoader.java?p2=aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/TempBundleDelegatingClassLoader.java&p1=aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/unit/impl/TempBundleDelegatingClassLoader.java&r1=1680054&r2=1680218&rev=1680218&view=diff ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/unit/impl/TempBundleDelegatingClassLoader.java (original) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/parser/impl/TempBundleDelegatingClassLoader.java Tue May 19 09:47:49 2015 @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.aries.jpa.container.unit.impl; +package org.apache.aries.jpa.container.parser.impl; import java.io.ByteArrayOutputStream; import java.io.IOException; Added: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/DummyDataSource.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/DummyDataSource.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/DummyDataSource.java (added) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/DummyDataSource.java Tue May 19 09:47:49 2015 @@ -0,0 +1,97 @@ +package org.apache.aries.jpa.container.weaving.impl; + +import java.io.PrintWriter; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; + +import javax.sql.DataSource; + +/** + * DummyDataSource to be able to create the EMF before DataSource is ready + */ +public final class DummyDataSource implements DataSource { + + /** + * Simply tries to avoid that calling code runs into NPE + */ + private final class DummyHandler implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + ClassLoader classLoader = this.getClass().getClassLoader(); + if (method.getReturnType() == DatabaseMetaData.class) { + Class<?>[] ifAr = new Class[] { + DatabaseMetaData.class + }; + return Proxy.newProxyInstance(classLoader, ifAr, this); + } + if (method.getReturnType() == int.class) { + return new Integer(0); + } + if (method.getReturnType() == boolean.class) { + return new Boolean(false); + } + if (method.getReturnType() == String.class) { + return ""; + } + if (method.getReturnType() == ResultSet.class) { + Class<?>[] ifAr = new Class[] { + ResultSet.class + }; + return Proxy.newProxyInstance(classLoader, ifAr, this); + } + return null; + } + } + + @Override + public <T> T unwrap(Class<T> iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class<?> iface) throws SQLException { + return false; + } + + @Override + public void setLoginTimeout(int seconds) throws SQLException { + } + + @Override + public void setLogWriter(PrintWriter out) throws SQLException { + } + + @Override + public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null; + } + + @Override + public int getLoginTimeout() throws SQLException { + return 0; + } + + @Override + public PrintWriter getLogWriter() throws SQLException { + return null; + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + InvocationHandler handler = new DummyHandler(); + ClassLoader classLoader = this.getClass().getClassLoader(); + return (Connection)Proxy.newProxyInstance(classLoader, new Class[] { + Connection.class + }, handler); + } + + @Override + public Connection getConnection() throws SQLException { + return getConnection(null, null); + } +} Modified: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/JPAWeavingHook.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/JPAWeavingHook.java?rev=1680218&r1=1680217&r2=1680218&view=diff ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/JPAWeavingHook.java (original) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/JPAWeavingHook.java Tue May 19 09:47:49 2015 @@ -20,112 +20,115 @@ package org.apache.aries.jpa.container.w import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashSet; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.Map; import javax.persistence.spi.ClassTransformer; -import org.apache.aries.jpa.container.impl.NLS; import org.osgi.framework.Bundle; -import org.osgi.framework.ServiceReference; import org.osgi.framework.hooks.weaving.WeavingException; import org.osgi.framework.hooks.weaving.WeavingHook; import org.osgi.framework.hooks.weaving.WovenClass; import org.osgi.framework.wiring.BundleWiring; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * This weaving hook delegates to any registered {@link ClassTransformer} instances - * for a given bundle + * This weaving hook delegates to any registered {@link ClassTransformer} instances for a given bundle */ public class JPAWeavingHook implements WeavingHook, TransformerRegistry { + private static final Logger LOGGER = LoggerFactory.getLogger(JPAWeavingHook.class); - /** - * This constructor should not be called directly, the {@link JPAWeavingHookFactory} - * should be used to ensure that Weaving support is available. - */ - JPAWeavingHook() { } - - /** - * With luck we will only have one persistence unit per bundle, but - * if we don't we'll need to call them until one of them does a transform - * or we run out. - */ - private final ConcurrentMap<Bundle, LinkedHashSet<WrappingTransformer>> registeredTransformers - = new ConcurrentHashMap<Bundle, LinkedHashSet<WrappingTransformer>>(); - - public void weave(WovenClass wovenClass) { - - BundleWiring wiring = wovenClass.getBundleWiring(); - - Collection<WrappingTransformer> transformers = registeredTransformers.get( - wiring.getBundle()); - - if(transformers != null) { - Collection<WrappingTransformer> transformersToTry; - synchronized (transformers) { - transformersToTry = new ArrayList<WrappingTransformer>(transformers); - } - for(WrappingTransformer transformer : transformersToTry) { + /** + * This constructor should not be called directly, the {@link JPAWeavingHookFactory} should be used to + * ensure that Weaving support is available. + */ + JPAWeavingHook() { + } + + /** + * With luck we will only have one persistence unit per bundle, but if we don't we'll need to call them + * until one of them does a transform or we run out. + */ + private final Map<Bundle, LinkedHashSet<ClassTransformer>> registeredTransformers = new HashMap<Bundle, LinkedHashSet<ClassTransformer>>(); + + public void weave(WovenClass wovenClass) { + BundleWiring wiring = wovenClass.getBundleWiring(); + Bundle bundle = wiring.getBundle(); + ClassLoader cl = wiring.getClassLoader(); + Collection<ClassTransformer> transformersToTry = getTransformers(bundle); + if (transformersToTry.size() == 0 && wovenClass.getClassName().endsWith("Car")) { + LOGGER.error("Loading " + wovenClass.getClassName() + " before transformer is present"); + //for (StackTraceElement el : Thread.currentThread().getStackTrace()) { +// LOGGER.info(el.toString()); +// } + } + for (ClassTransformer transformer : transformersToTry) { + + if (transformClass(wovenClass, cl, transformer)) { + LOGGER.info("Weaving " + wovenClass.getClassName() + " using " + transformer.getClass().getName()); + break; + }; + } + } + + @SuppressWarnings("unchecked") + private synchronized Collection<ClassTransformer> getTransformers(Bundle bundle) { + LinkedHashSet<ClassTransformer> transformers = registeredTransformers.get(bundle); + return transformers != null ? new ArrayList<ClassTransformer>(transformers) : Collections.EMPTY_LIST; + } + + private boolean transformClass(WovenClass wovenClass, ClassLoader cl, ClassTransformer transformer) + throws ThreadDeath, OutOfMemoryError { try { - byte[] result = transformer.transform(wiring.getClassLoader(), - wovenClass.getClassName(), wovenClass.getDefinedClass(), - wovenClass.getProtectionDomain(), wovenClass.getBytes()); - if(result != null) { - wovenClass.setBytes(result); - wovenClass.getDynamicImports().addAll(transformer.getPackagesToAdd()); - break; - } + byte[] result = transformer + .transform(cl, + wovenClass.getClassName(), + wovenClass.getDefinedClass(), + wovenClass.getProtectionDomain(), + wovenClass.getBytes()); + if (result != null) { + wovenClass.setBytes(result); + wovenClass.getDynamicImports().add("org.eclipse.persistence.*"); + wovenClass.getDynamicImports().add("org.apache.openjpa.*"); + + return true; + } } catch (Throwable t) { - if(t instanceof ThreadDeath) - throw (ThreadDeath)t; - else if (t instanceof OutOfMemoryError) - throw (OutOfMemoryError) t; - else { - Bundle b = wovenClass.getBundleWiring().getBundle(); - throw new WeavingException(NLS.MESSAGES.getMessage("jpa.weaving.failure", wovenClass.getClassName(), b.getSymbolicName(), b.getVersion(), transformer), t); - } - } - } - } - } - - public void addTransformer(Bundle pBundle, ClassTransformer transformer, ServiceReference<?> provider) { - - //Optimised for single adds - - LinkedHashSet<WrappingTransformer> set = new LinkedHashSet<WrappingTransformer>(); - WrappingTransformer wt = new WrappingTransformer(transformer, provider); - set.add(wt); - - LinkedHashSet<WrappingTransformer> existingSet = registeredTransformers.putIfAbsent(pBundle, set); - - if(existingSet != null) { - synchronized (existingSet) { - existingSet.add(wt); - } - } - } - - public void removeTransformer(Bundle pBundle, ClassTransformer transformer) { - LinkedHashSet<WrappingTransformer> set = registeredTransformers.get(pBundle); - - if(set == null || !!!safeRemove(set, transformer)) - throw new IllegalStateException(NLS.MESSAGES.getMessage("jpa.weaving.transformer.not.registered", transformer)); - - if(set.isEmpty()) - registeredTransformers.remove(pBundle); - } - - /** - * Perform a remove on the collection while synchronized on it - * @param set - * @param t - * @return - */ - private boolean safeRemove(Collection<WrappingTransformer> set, ClassTransformer t) { - synchronized(set) { - return set.remove(new WrappingTransformer(t)); - } - } + if (t instanceof ThreadDeath) + throw (ThreadDeath)t; + else if (t instanceof OutOfMemoryError) + throw (OutOfMemoryError)t; + else { + Bundle b = wovenClass.getBundleWiring().getBundle(); + String msg = String.format("Weaving failure", wovenClass.getClassName(), + b.getSymbolicName(), b.getVersion(), transformer); + throw new WeavingException(msg, t); + } + } + return false; + } + + public synchronized void addTransformer(Bundle pBundle, ClassTransformer transformer) { + LOGGER.info("Adding transformer " + transformer.getClass().getName()); + LinkedHashSet<ClassTransformer> transformers = registeredTransformers.get(pBundle); + if (transformers == null) { + transformers = new LinkedHashSet<ClassTransformer>(); + registeredTransformers.put(pBundle, transformers); + } + transformers.add(transformer); + } + + public synchronized void removeTransformer(Bundle pBundle, ClassTransformer transformer) { + LinkedHashSet<ClassTransformer> set = registeredTransformers.get(pBundle); + if (set == null || !set.remove(transformer)) { + throw new IllegalStateException("Transformer " + transformer + " not registered"); + } + if (set.isEmpty()) { + registeredTransformers.remove(pBundle); + } + } + } Modified: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistry.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistry.java?rev=1680218&r1=1680217&r2=1680218&view=diff ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistry.java (original) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistry.java Tue May 19 09:47:49 2015 @@ -21,11 +21,10 @@ package org.apache.aries.jpa.container.w import javax.persistence.spi.ClassTransformer; import org.osgi.framework.Bundle; -import org.osgi.framework.ServiceReference; /** * {@link ClassTransformer} instances should be registered with the - * instance of this interface returned by {@link TransformerRegistryFactory#getTransformerRegistry()} + * instance of this interface returned by {@link TransformerRegistrySingleton#getTransformerRegistry()} */ public interface TransformerRegistry { @@ -36,7 +35,7 @@ public interface TransformerRegistry { * @param transformer The transformer to weave with * @param provider The provider to provide packages from */ - public void addTransformer(Bundle pBundle, ClassTransformer transformer, ServiceReference<?> provider); + public void addTransformer(Bundle pBundle, ClassTransformer transformer); /** Copied: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistrySingleton.java (from r1680054, aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/quiesce/impl/DestroyCallback.java) URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistrySingleton.java?p2=aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistrySingleton.java&p1=aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/quiesce/impl/DestroyCallback.java&r1=1680054&r2=1680218&rev=1680218&view=diff ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/quiesce/impl/DestroyCallback.java (original) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/TransformerRegistrySingleton.java Tue May 19 09:47:49 2015 @@ -16,11 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.aries.jpa.container.quiesce.impl; +package org.apache.aries.jpa.container.weaving.impl; + /** - * An asynchronous callback for destroying something + * This class is used to get hold of the active {@link TransformerRegistry} for this bundle. */ -public interface DestroyCallback { - public void callback(); +public class TransformerRegistrySingleton { + private static TransformerRegistry _instance; + + public static TransformerRegistry get() { + if (_instance == null) { + _instance = new JPAWeavingHook(); + } + return _instance; + } } Modified: aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/WrappingTransformer.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/WrappingTransformer.java?rev=1680218&r1=1680217&r2=1680218&view=diff ============================================================================== --- aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/WrappingTransformer.java (original) +++ aries/trunk/jpa/jpa-container/src/main/java/org/apache/aries/jpa/container/weaving/impl/WrappingTransformer.java Tue May 19 09:47:49 2015 @@ -25,7 +25,6 @@ import java.util.HashSet; import javax.persistence.spi.ClassTransformer; -import org.apache.aries.jpa.container.impl.NLS; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; @@ -34,65 +33,63 @@ import org.osgi.framework.wiring.BundleR import org.osgi.framework.wiring.BundleWiring; class WrappingTransformer implements ClassTransformer { - private final ClassTransformer delegate; - private final Collection<String> packageImportsToAdd = new HashSet<String>(); - - public WrappingTransformer(ClassTransformer delegate, - ServiceReference<?> persistenceProvider) { - - if(delegate == null) - throw new NullPointerException(NLS.MESSAGES.getMessage("jpa.weaving.null.transformer")); - - if(persistenceProvider == null) { - throw new NullPointerException(NLS.MESSAGES.getMessage("jpa.weaving.null.provider")); - } - - this.delegate = delegate; - - Object packages = persistenceProvider.getProperty("org.apache.aries.jpa.container.weaving.packages"); - - if(packages instanceof String[]) { - for(String s : (String[]) packages) { - packageImportsToAdd.add(s); - } - } else { - Bundle provider = persistenceProvider.getBundle(); - String suffix = ";" + Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE + "=" + - provider.getSymbolicName() + ";" + Constants.BUNDLE_VERSION_ATTRIBUTE - + "=" + provider.getVersion(); - - BundleRevision br = provider.adapt(BundleWiring.class).getRevision(); - for(BundleCapability bc : br.getDeclaredCapabilities(BundleRevision.PACKAGE_NAMESPACE)) { - packageImportsToAdd.add(bc.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE) + suffix); - } - } - } - - public WrappingTransformer(ClassTransformer transformer) { - delegate = transformer; - } - - public byte[] transform(ClassLoader arg0, String arg1, Class<?> arg2, - ProtectionDomain arg3, byte[] arg4) throws IllegalClassFormatException { - return delegate.transform(arg0, arg1, arg2, arg3, arg4); - } - - public Collection<String> getPackagesToAdd() { - return packageImportsToAdd; - } - - public int hashCode() { - return delegate.hashCode(); - } - - public boolean equals(Object o) { - if(o instanceof WrappingTransformer) - return delegate == ((WrappingTransformer) o).delegate; - - return false; - } - - public String toString() { - return "Transformer: " + delegate.toString() + " Packages to add: " + packageImportsToAdd; - } -} \ No newline at end of file + private final ClassTransformer delegate; + private final Collection<String> packageImportsToAdd = new HashSet<String>(); + + public WrappingTransformer(ClassTransformer delegate, ServiceReference<?> persistenceProvider) { + + if (delegate == null) + throw new NullPointerException("Transformer delegate may not be null"); + + if (persistenceProvider == null) { + throw new NullPointerException("PersistenceProvider may not be null"); + } + + this.delegate = delegate; + + Object packages = persistenceProvider.getProperty("org.apache.aries.jpa.container.weaving.packages"); + + if (packages instanceof String[]) { + for (String s : (String[])packages) { + packageImportsToAdd.add(s); + } + } else { + Bundle provider = persistenceProvider.getBundle(); + String suffix = ";" + Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE + "=" + provider.getSymbolicName() + + ";" + Constants.BUNDLE_VERSION_ATTRIBUTE + "=" + provider.getVersion(); + + BundleRevision br = provider.adapt(BundleWiring.class).getRevision(); + for (BundleCapability bc : br.getDeclaredCapabilities(BundleRevision.PACKAGE_NAMESPACE)) { + packageImportsToAdd.add(bc.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE) + suffix); + } + } + } + + public WrappingTransformer(ClassTransformer transformer) { + delegate = transformer; + } + + public byte[] transform(ClassLoader arg0, String arg1, Class<?> arg2, ProtectionDomain arg3, byte[] arg4) + throws IllegalClassFormatException { + return delegate.transform(arg0, arg1, arg2, arg3, arg4); + } + + public Collection<String> getPackagesToAdd() { + return packageImportsToAdd; + } + + public int hashCode() { + return delegate.hashCode(); + } + + public boolean equals(Object o) { + if (o instanceof WrappingTransformer) + return delegate == ((WrappingTransformer)o).delegate; + + return false; + } + + public String toString() { + return "Transformer: " + delegate.toString() + " Packages to add: " + packageImportsToAdd; + } +} Added: aries/trunk/jpa/jpa-container/src/test/java/org/apache/aries/jpa/container/impl/DataSourceTrackerTest.java URL: http://svn.apache.org/viewvc/aries/trunk/jpa/jpa-container/src/test/java/org/apache/aries/jpa/container/impl/DataSourceTrackerTest.java?rev=1680218&view=auto ============================================================================== --- aries/trunk/jpa/jpa-container/src/test/java/org/apache/aries/jpa/container/impl/DataSourceTrackerTest.java (added) +++ aries/trunk/jpa/jpa-container/src/test/java/org/apache/aries/jpa/container/impl/DataSourceTrackerTest.java Tue May 19 09:47:49 2015 @@ -0,0 +1,60 @@ +/* + * 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.impl; + +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.persistence.spi.PersistenceUnitTransactionType; + +import org.apache.aries.jpa.container.impl.DataSourceTracker; +import org.apache.aries.jpa.container.parser.impl.PersistenceUnit; +import org.junit.Test; +import org.mockito.Mockito; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; + +public class DataSourceTrackerTest { + + @Test + public void testCreateFilterFull() throws InvalidSyntaxException { + PersistenceUnit punit = mock(PersistenceUnit.class); + when(punit.getJtaDataSourceName()).thenReturn("osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=tasklist)"); + when(punit.getTransactionType()).thenReturn(PersistenceUnitTransactionType.JTA); + BundleContext context = mock(BundleContext.class); + + DataSourceTracker.createFilter(context, punit); + + verify(context, atLeastOnce()).createFilter(Mockito.eq("(&(objectClass=javax.sql.DataSource)(osgi.jndi.service.name=tasklist))")); + } + + @Test + public void testCreateFilterSimple() throws InvalidSyntaxException { + PersistenceUnit punit = mock(PersistenceUnit.class); + when(punit.getJtaDataSourceName()).thenReturn("tasklist"); + when(punit.getTransactionType()).thenReturn(PersistenceUnitTransactionType.JTA); + BundleContext context = mock(BundleContext.class); + + DataSourceTracker.createFilter(context, punit); + + verify(context, atLeastOnce()).createFilter(Mockito.eq("(&(objectClass=javax.sql.DataSource)(osgi.jndi.service.name=tasklist))")); + } +}
