/*******************************************************************************
* Copyright (c) 1998, 2009 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* tware, ssmith = 1.0 - Initialize Persistence in OSGI
******************************************************************************/
package org.eclipse.persistence.internal.jpa.deployment.osgi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.exceptions.EntityManagerSetupException;
import org.eclipse.persistence.internal.jpa.deployment.JPAInitializer;
import org.eclipse.persistence.internal.jpa.deployment.PersistenceInitializationHelper;
import org.eclipse.persistence.internal.localization.LoggingLocalization;
import org.eclipse.persistence.jpa.osgi.Activator;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
* Overrides the default persistence initialization behavior to allow classloaders to be obtained
* from their appropriate bundle
*
* @see PersistenceInitialization helper
*
* @author tware
* @author shsmith
*
*/
public class OSGiPersistenceInitializationHelper extends PersistenceInitializationHelper {
private String initializerClassName = null;
public static final String EQUINOX_INITIALIZER_NAME = "org.eclipse.persistence.internal.jpa.deployment.osgi.equinox.EquinoxInitializer";
private static final String ORG_ECLIPSE_PERSISTENCE_CORE = "org.eclipse.persistence.core";
// these maps are used to retrieve the classloader used for different bundles
private static Map<String, Bundle> puToBundle = Collections.synchronizedMap(new HashMap<String,Bundle>());
private static Map<Bundle, String[]> bundleToPUs = Collections.synchronizedMap(new HashMap<Bundle, String[]>());
private Map<String,ClassLoader> puClassLoaders = new HashMap<String, ClassLoader>();
/**
* Add a bundle to the list of bundles managed by this persistence provider
* The bundle is indexed so it's classloader can be accessed
* @param bundle
* @param persistenceUnitNames
*/
public static void addBundle(Bundle bundle, String[] persistenceUnitNames) {
for (int i = 0; i < persistenceUnitNames.length; i++) {
String name = persistenceUnitNames[i];
puToBundle.put(name, bundle);
}
bundleToPUs.put(bundle, persistenceUnitNames);
}
/**
* Removed a bundle from the list of bundles managed by this persistence provider
* This typically happens on deactivation.
* @param bundle
*/
public static void removeBundle(Bundle bundle) {
String[] persistenceUnitNames = bundleToPUs.remove(bundle);
if (persistenceUnitNames != null) {
for (int i = 0; i < persistenceUnitNames.length; i++) {
String name = persistenceUnitNames[i];
puToBundle.remove(name);
}
}
}
public OSGiPersistenceInitializationHelper(String initializerClassName){
this.initializerClassName = initializerClassName;
}
/**
* Answer the ClassLoader to use to create an EntityManager.
* The result is a CompositeClassLoader capable of loading
* classes from the optionally provided ClassLoader, the
* bundle that provides the persistence unit (i.e., contains the
* persistence.xml, and EclipseLink classes (since the
* persistence unit bundle may not have an explicit dependency
* on EclipseLink).
*
* @param persistenceUnitName
* @param properties
* @return ClassLoader
*/
public ClassLoader getClassLoader(String persistenceUnitName, Map properties) {
// This method is called more than once for the same persistence unit so
// check to see if a ClassLoader already constructed and if so return it.
ClassLoader previouslyDefinedLoader = puClassLoaders.get(persistenceUnitName);
if (previouslyDefinedLoader != null) {
return previouslyDefinedLoader;
}
List<ClassLoader> loaders = new ArrayList<ClassLoader>();
ClassLoader propertyClassLoader = null;
// Look for a ClassLoader in the properties.
if (properties != null) {
Object propertyValue = properties.get(PersistenceUnitProperties.CLASSLOADER);
if ((propertyValue != null) && (propertyValue instanceof ClassLoader)) {
propertyClassLoader = (ClassLoader)propertyValue;
loaders.add(propertyClassLoader);
}
}
// If a bundle has registered for a persistence unit
// then wrap the bundle to support the ClassLoader interface.
Bundle bundle = puToBundle.get(persistenceUnitName);
if (bundle != null) {
ClassLoader bundleClassLoader = new BundleProxyClassLoader(bundle);
loaders.add(bundleClassLoader);
}
if ((propertyClassLoader == null) && (bundle == null)) {
throw EntityManagerSetupException.couldNotFindPersistenceUnitBundle(persistenceUnitName);
}
// Add the EclipseLink Core bundle loader so we can see classes
// (such as platforms) in fragments,
BundleContext context = Activator.getContext();
if (context != null) {
// context is set in the Activator which may might
// not be called in an RCP application
Bundle[] bundles = context.getBundles();
Bundle coreBundle = null;
for (int i = 0; i < bundles.length; i++) {
if (bundles[i].getSymbolicName().equals(ORG_ECLIPSE_PERSISTENCE_CORE)) {
coreBundle = bundles[i];
loaders.add(new BundleProxyClassLoader(coreBundle));
break;
}
}
// Add the EclipseLink JPA bundle ClassLoader so that we can load
// EclipseLink classes.
ClassLoader eclipseLinkJpaClassLoader = new BundleProxyClassLoader(context.getBundle());
loaders.add(eclipseLinkJpaClassLoader);
}
// If the only classloader is the one passes as a property
// then no point building a composite--just use the one passed.
ClassLoader puClassLoader = null;
if ((loaders.size() == 1) && (loaders.get(0) == propertyClassLoader)) {
puClassLoader = propertyClassLoader;
} else {
puClassLoader = new CompositeClassLoader(loaders);
}
puClassLoaders.put(persistenceUnitName, puClassLoader); // cache to avoid reconstruction
return puClassLoader;
}
/**
* Get the initializer class
* Here we will attempt to build an EquinoxInitializer. It will only be available if the org.eclipse.persistence.jpa.equinox
* fragment is available. Else, we will return a standard OSGi initializer
*
*/
public JPAInitializer getInitializer(ClassLoader classLoader, Map m){
if (initializerClassName != null){
try{
// try to build the passed-in initializer. If it is available, we will build it otherwise
// we will assume generic OSGI
Class initializerClass = Class.forName(initializerClassName);
Class[] argTypes = new Class[]{ClassLoader.class, Map.class, PersistenceInitializationHelper.class};
Object[] args = new Object[]{classLoader, m, this};
JPAInitializer initializer = (JPAInitializer)initializerClass.getConstructor(argTypes).newInstance(args);
return initializer;
} catch (Exception e){
AbstractSessionLog.getLog().log(SessionLog.FINEST, LoggingLocalization.buildMessage("osgi_initializer_failed", new Object[]{initializerClassName, e}));
};
}
return new OSGiInitializer(classLoader);
}
public static boolean includesBundle(Bundle bundle) {
return bundleToPUs.containsKey(bundle);
}
}