OPENJPA-2057: Add additional class loader to configuration to load custom plugins

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1425072 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Pinaki Poddar 2012-12-21 18:29:38 +00:00
parent 56107fc73c
commit 0a8d89da00
4 changed files with 100 additions and 85 deletions

View File

@ -29,6 +29,7 @@ import java.util.Set;
import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.log.LogFactory; import org.apache.openjpa.lib.log.LogFactory;
import org.apache.openjpa.lib.util.Closeable; import org.apache.openjpa.lib.util.Closeable;
import org.apache.openjpa.persistence.PersistenceProviderImpl;
/** /**
* Interface for generic configuration objects. Includes the ability * Interface for generic configuration objects. Includes the ability
@ -252,26 +253,22 @@ public interface Configuration
public Object clone(); public Object clone();
/** /**
* Modifies a <em>dynamic</em> property of this receiver even when * Gets a class loader that can be additionally used to load custom plugin values.
* {@link #setReadOnly(boolean) frozen}. *
* * @see Configurations#newInstance(String, ClassLoader)
* @since 1.0.0 * @return an additional classloader for loading custom plugins. Can be null.
* @since 2.3.0
*/ */
// public void modifyDynamic(String property, Object newValue); ClassLoader getUserClassLoader();
//
// /** /**
// * Affirms if the given property can be modified <em>dynamically</em> i.e. * Sets an additional classloader to load custom plugin values.
// * even after the receiver is {@link #setReadOnly(boolean) frozen}. * In OSGi environment, we internally set the bundle class loader as
// * * the user class loader.
// * @since 1.0.0 *
// */ * @param loader a class loader to load custom plugin values
// public boolean isDynamic(String property); * @see PersistenceProviderImpl#createEntityManagerFactory(String, Map)
// * @since 2.3.0
// /** */
// * Gets the values that can be modified <em>dynamically</em> i.e. void setUserClassLoader(ClassLoader loader);
// * even after the receiver is {@link #setReadOnly(boolean) frozen}.
// *
// * @since 1.0.0
// */
// public Value[] getDynamicValues();
} }

View File

@ -106,8 +106,7 @@ public class ConfigurationImpl
private static final String SEP = J2DoPrivHelper.getLineSeparator(); private static final String SEP = J2DoPrivHelper.getLineSeparator();
private static final Localizer _loc = Localizer.forPackage private static final Localizer _loc = Localizer.forPackage(ConfigurationImpl.class);
(ConfigurationImpl.class);
public ObjectValue logFactoryPlugin; public ObjectValue logFactoryPlugin;
public StringValue id; public StringValue id;
@ -126,10 +125,15 @@ public class ConfigurationImpl
// cache descriptors // cache descriptors
private PropertyDescriptor[] _pds = null; private PropertyDescriptor[] _pds = null;
private MethodDescriptor[] _mds = null; private MethodDescriptor[] _mds = null;
// An additional (and optional) classloader to load custom plugins.
private ClassLoader _userCL;
//Ant task needs to defer the resource loading //Ant task needs to defer the resource loading
//until the classpath setting is loaded properly //until the classpath setting is loaded properly
private boolean _deferResourceLoading = false; private boolean _deferResourceLoading = false;
/** /**
* Default constructor. Attempts to load default properties through * Default constructor. Attempts to load default properties through
* system's configured {@link ProductDerivation}s. * system's configured {@link ProductDerivation}s.
@ -1107,4 +1111,12 @@ public class ConfigurationImpl
addValue(val); addValue(val);
return val; return val;
} }
public ClassLoader getUserClassLoader() {
return _userCL;
}
public void setUserClassLoader(ClassLoader cl) {
_userCL = cl;
}
} }

View File

@ -21,6 +21,7 @@ package org.apache.openjpa.lib.conf;
import java.io.File; import java.io.File;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedActionException; import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -186,6 +187,34 @@ public class Configurations {
return obj; return obj;
} }
/**
* Loads the given class name by the given loader.
* For efficiency, a cache per class loader is maintained of classes already loader.
* @param clsName
* @param loader
* @return
*/
static Class<?> loadClass(String clsName, ClassLoader loader) {
Class<?> cls = null;
Object key = loader == null ? NULL_LOADER : loader;
Map<String,Class<?>> loaderCache = (Map<String,Class<?>>) _loaders.get(key);
if (loaderCache == null) { // We don't have a cache for this loader.
loaderCache = new ConcurrentHashMap<String,Class<?>>();
_loaders.put(key, loaderCache);
} else { // We have a cache for this loader.
cls = (Class<?>) loaderCache.get(clsName);
}
if (cls == null) {
try {
cls = Strings.toClass(clsName, loader);
loaderCache.put(clsName, cls);
} catch (RuntimeException re) {
}
}
return cls;
}
/** /**
* Helper method used by members of this package to instantiate plugin * Helper method used by members of this package to instantiate plugin
* values. * values.
@ -195,53 +224,30 @@ public class Configurations {
if (StringUtils.isEmpty(clsName)) if (StringUtils.isEmpty(clsName))
return null; return null;
Class cls = null; Class<?> cls = loadClass(clsName, findDerivedLoader(conf, loader));
if (cls == null) {
while (cls == null) { cls = loadClass(clsName, findDerivedLoader(conf, null));
// can't have a null reference in the map, so use symbolic }
// constant as key if (cls == null && conf.getUserClassLoader() != null) {
Object key = loader == null ? NULL_LOADER : loader; cls = loadClass(clsName, conf.getUserClassLoader());
Map loaderCache = (Map) _loaders.get(key);
if (loaderCache == null) { // We don't have a cache for this loader.
loaderCache = new ConcurrentHashMap();
_loaders.put(key, loaderCache);
} else { // We have a cache for this loader.
cls = (Class) loaderCache.get(clsName);
}
if (cls == null) {
try {
cls = Strings.toClass(clsName, findDerivedLoader(conf,
loader));
loaderCache.put(clsName, cls);
} catch (RuntimeException re) {
if (loader != null) // Try one more time with loader=null
loader = null;
else {
if (val != null)
re = getCreateException(clsName, val, re);
if (fatal)
throw re;
Log log = (conf == null) ? null : conf
.getConfigurationLog();
if (log != null && log.isErrorEnabled())
log.error(_loc
.get("plugin-creation-exception", val), re);
return null;
}
}
}
} }
if (cls == null) {
if (fatal)
throw getCreateException(clsName, val, new ClassNotFoundException(clsName));
Log log = (conf == null) ? null : conf.getConfigurationLog();
if (log != null && log.isErrorEnabled())
log.error(_loc.get("plugin-creation-exception", val));
return null;
}
try { try {
return AccessController.doPrivileged( return AccessController.doPrivileged(J2DoPrivHelper.newInstanceAction(cls));
J2DoPrivHelper.newInstanceAction(cls));
} catch (Exception e) { } catch (Exception e) {
if (e instanceof PrivilegedActionException) { if (e instanceof PrivilegedActionException) {
e = ((PrivilegedActionException) e).getException(); e = ((PrivilegedActionException) e).getException();
} }
RuntimeException re = new NestableRuntimeException(_loc.get RuntimeException re = new NestableRuntimeException(_loc.get("obj-create", cls).getMessage(), e);
("obj-create", cls).getMessage(), e);
if (fatal) if (fatal)
throw re; throw re;
Log log = (conf == null) ? null : conf.getConfigurationLog(); Log log = (conf == null) ? null : conf.getConfigurationLog();
@ -256,42 +262,42 @@ public class Configurations {
* This allows application loaders that delegate appropriately for known * This allows application loaders that delegate appropriately for known
* classes first crack at class names. * classes first crack at class names.
*/ */
private static ClassLoader findDerivedLoader(Configuration conf, private static ClassLoader findDerivedLoader(Configuration conf, ClassLoader loader) {
ClassLoader loader) {
// we always prefer the thread loader, because it's the only thing we // we always prefer the thread loader, because it's the only thing we
// can access that isn't bound to the OpenJPA classloader, unless // can access that isn't bound to the OpenJPA classloader, unless
// the conf object is of a custom class // the conf object is of a custom class
ClassLoader ctxLoader = AccessController.doPrivileged( ClassLoader ctxLoader = AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction());
J2DoPrivHelper.getContextClassLoaderAction());
if (loader == null) { if (loader == null) {
if (ctxLoader != null) if (ctxLoader != null) {
return ctxLoader; return ctxLoader;
if (conf != null) } else if (conf != null) {
return AccessController.doPrivileged( return classLoaderOf(conf.getClass());
J2DoPrivHelper.getClassLoaderAction(conf.getClass())); } else {
return Configurations.class.getClassLoader(); return classLoaderOf(Configurations.class);
}
} }
for (ClassLoader parent = ctxLoader; parent != null; for (ClassLoader parent = ctxLoader; parent != null; parent = parentClassLoaderOf(parent)) {
parent = AccessController.doPrivileged(
J2DoPrivHelper.getParentAction(parent))) {
if (parent == loader) if (parent == loader)
return ctxLoader; return ctxLoader;
} }
if (conf != null) { if (conf != null) {
for (ClassLoader parent = (ClassLoader) for (ClassLoader parent = classLoaderOf(conf.getClass()); parent != null;
AccessController.doPrivileged( parent = parentClassLoaderOf(parent)) {
J2DoPrivHelper.getClassLoaderAction(conf.getClass()));
parent != null;
parent = AccessController.doPrivileged(
J2DoPrivHelper.getParentAction(parent))) {
if (parent == loader) if (parent == loader)
return AccessController.doPrivileged( return classLoaderOf(conf.getClass());
J2DoPrivHelper.getClassLoaderAction(conf.getClass()));
} }
} }
return loader; return loader;
} }
static ClassLoader classLoaderOf(Class<?> cls) {
return AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(cls));
}
static ClassLoader parentClassLoaderOf(ClassLoader loader) {
return AccessController.doPrivileged(J2DoPrivHelper.getParentAction(loader));
}
/** /**
* Return a List<String> of all the fully-qualified anchors specified in the * Return a List<String> of all the fully-qualified anchors specified in the
@ -386,8 +392,7 @@ public class Configurations {
/** /**
* Helper method to throw an informative description on instantiation error. * Helper method to throw an informative description on instantiation error.
*/ */
private static RuntimeException getCreateException(String clsName, private static RuntimeException getCreateException(String clsName, Value val, Exception e) {
Value val, Exception e) {
// re-throw the exception with some better information // re-throw the exception with some better information
final String msg; final String msg;
final Object[] params; final Object[] params;

View File

@ -90,6 +90,7 @@ public class PersistenceProviderImpl
BrokerFactory factory = getBrokerFactory(cp, poolValue, BundleUtils.getBundleClassLoader()); BrokerFactory factory = getBrokerFactory(cp, poolValue, BundleUtils.getBundleClassLoader());
OpenJPAConfiguration conf = factory.getConfiguration(); OpenJPAConfiguration conf = factory.getConfiguration();
conf.setUserClassLoader(BundleUtils.getBundleClassLoader());
_log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME); _log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
pd.checkPuNameCollisions(_log,name); pd.checkPuNameCollisions(_log,name);