From 0a8d89da00391980018b6ce1acd80fde98f8ec78 Mon Sep 17 00:00:00 2001 From: Pinaki Poddar Date: Fri, 21 Dec 2012 18:29:38 +0000 Subject: [PATCH] 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 --- .../openjpa/lib/conf/Configuration.java | 39 +++--- .../openjpa/lib/conf/ConfigurationImpl.java | 16 ++- .../openjpa/lib/conf/Configurations.java | 129 +++++++++--------- .../persistence/PersistenceProviderImpl.java | 1 + 4 files changed, 100 insertions(+), 85 deletions(-) diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java index dc9749d63..250b81b4b 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.log.LogFactory; import org.apache.openjpa.lib.util.Closeable; +import org.apache.openjpa.persistence.PersistenceProviderImpl; /** * Interface for generic configuration objects. Includes the ability @@ -252,26 +253,22 @@ public interface Configuration public Object clone(); /** - * Modifies a dynamic property of this receiver even when - * {@link #setReadOnly(boolean) frozen}. - * - * @since 1.0.0 + * Gets a class loader that can be additionally used to load custom plugin values. + * + * @see Configurations#newInstance(String, ClassLoader) + * @return an additional classloader for loading custom plugins. Can be null. + * @since 2.3.0 */ -// public void modifyDynamic(String property, Object newValue); -// -// /** -// * Affirms if the given property can be modified dynamically i.e. -// * even after the receiver is {@link #setReadOnly(boolean) frozen}. -// * -// * @since 1.0.0 -// */ -// public boolean isDynamic(String property); -// -// /** -// * Gets the values that can be modified dynamically i.e. -// * even after the receiver is {@link #setReadOnly(boolean) frozen}. -// * -// * @since 1.0.0 -// */ -// public Value[] getDynamicValues(); + ClassLoader getUserClassLoader(); + + /** + * Sets an additional classloader to load custom plugin values. + * In OSGi environment, we internally set the bundle class loader as + * the user class loader. + * + * @param loader a class loader to load custom plugin values + * @see PersistenceProviderImpl#createEntityManagerFactory(String, Map) + * @since 2.3.0 + */ + void setUserClassLoader(ClassLoader loader); } diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java index 7447bb025..a311b6a0b 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java @@ -106,8 +106,7 @@ public class ConfigurationImpl private static final String SEP = J2DoPrivHelper.getLineSeparator(); - private static final Localizer _loc = Localizer.forPackage - (ConfigurationImpl.class); + private static final Localizer _loc = Localizer.forPackage(ConfigurationImpl.class); public ObjectValue logFactoryPlugin; public StringValue id; @@ -126,10 +125,15 @@ public class ConfigurationImpl // cache descriptors private PropertyDescriptor[] _pds = 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 //until the classpath setting is loaded properly private boolean _deferResourceLoading = false; + /** * Default constructor. Attempts to load default properties through * system's configured {@link ProductDerivation}s. @@ -1107,4 +1111,12 @@ public class ConfigurationImpl addValue(val); return val; } + + public ClassLoader getUserClassLoader() { + return _userCL; + } + + public void setUserClassLoader(ClassLoader cl) { + _userCL = cl; + } } diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurations.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurations.java index 81ac83329..7130c5867 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurations.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurations.java @@ -21,6 +21,7 @@ package org.apache.openjpa.lib.conf; import java.io.File; import java.security.AccessController; import java.security.PrivilegedActionException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -186,6 +187,34 @@ public class Configurations { 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> 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, loader); + loaderCache.put(clsName, cls); + } catch (RuntimeException re) { + + } + } + return cls; + } + /** * Helper method used by members of this package to instantiate plugin * values. @@ -195,53 +224,30 @@ public class Configurations { if (StringUtils.isEmpty(clsName)) return null; - Class cls = null; - - while (cls == null) { - // can't have a null reference in the map, so use symbolic - // constant as key - Object key = loader == null ? NULL_LOADER : loader; - 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; - } - } - } + Class cls = loadClass(clsName, findDerivedLoader(conf, loader)); + if (cls == null) { + cls = loadClass(clsName, findDerivedLoader(conf, null)); + } + if (cls == null && conf.getUserClassLoader() != null) { + cls = loadClass(clsName, conf.getUserClassLoader()); } + 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 { - return AccessController.doPrivileged( - J2DoPrivHelper.newInstanceAction(cls)); + return AccessController.doPrivileged(J2DoPrivHelper.newInstanceAction(cls)); } catch (Exception e) { if (e instanceof PrivilegedActionException) { e = ((PrivilegedActionException) e).getException(); } - RuntimeException re = new NestableRuntimeException(_loc.get - ("obj-create", cls).getMessage(), e); + RuntimeException re = new NestableRuntimeException(_loc.get("obj-create", cls).getMessage(), e); if (fatal) throw re; Log log = (conf == null) ? null : conf.getConfigurationLog(); @@ -256,42 +262,42 @@ public class Configurations { * This allows application loaders that delegate appropriately for known * classes first crack at class names. */ - private static ClassLoader findDerivedLoader(Configuration conf, - ClassLoader loader) { + private static ClassLoader findDerivedLoader(Configuration conf, ClassLoader loader) { // we always prefer the thread loader, because it's the only thing we // can access that isn't bound to the OpenJPA classloader, unless // the conf object is of a custom class - ClassLoader ctxLoader = AccessController.doPrivileged( - J2DoPrivHelper.getContextClassLoaderAction()); + ClassLoader ctxLoader = AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction()); if (loader == null) { - if (ctxLoader != null) + if (ctxLoader != null) { return ctxLoader; - if (conf != null) - return AccessController.doPrivileged( - J2DoPrivHelper.getClassLoaderAction(conf.getClass())); - return Configurations.class.getClassLoader(); + } else if (conf != null) { + return classLoaderOf(conf.getClass()); + } else { + return classLoaderOf(Configurations.class); + } } - for (ClassLoader parent = ctxLoader; parent != null; - parent = AccessController.doPrivileged( - J2DoPrivHelper.getParentAction(parent))) { + for (ClassLoader parent = ctxLoader; parent != null; parent = parentClassLoaderOf(parent)) { if (parent == loader) return ctxLoader; } if (conf != null) { - for (ClassLoader parent = (ClassLoader) - AccessController.doPrivileged( - J2DoPrivHelper.getClassLoaderAction(conf.getClass())); - parent != null; - parent = AccessController.doPrivileged( - J2DoPrivHelper.getParentAction(parent))) { + for (ClassLoader parent = classLoaderOf(conf.getClass()); parent != null; + parent = parentClassLoaderOf(parent)) { if (parent == loader) - return AccessController.doPrivileged( - J2DoPrivHelper.getClassLoaderAction(conf.getClass())); + return classLoaderOf(conf.getClass()); } } 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 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. */ - private static RuntimeException getCreateException(String clsName, - Value val, Exception e) { + private static RuntimeException getCreateException(String clsName, Value val, Exception e) { // re-throw the exception with some better information final String msg; final Object[] params; diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java index a73bb20ab..e15406acd 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java @@ -90,6 +90,7 @@ public class PersistenceProviderImpl BrokerFactory factory = getBrokerFactory(cp, poolValue, BundleUtils.getBundleClassLoader()); OpenJPAConfiguration conf = factory.getConfiguration(); + conf.setUserClassLoader(BundleUtils.getBundleClassLoader()); _log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME); pd.checkPuNameCollisions(_log,name);