Author: epbernard
Date: 2009-07-23 17:25:08 -0400 (Thu, 23 Jul 2009)
New Revision: 17199
Modified:
jpa-api/trunk/src/main/java/javax/persistence/spi/PersistenceProviderResolverHolder.java
Log:
EJB-454 implements the default PPR strategy
Modified:
jpa-api/trunk/src/main/java/javax/persistence/spi/PersistenceProviderResolverHolder.java
===================================================================
---
jpa-api/trunk/src/main/java/javax/persistence/spi/PersistenceProviderResolverHolder.java 2009-07-23
18:54:27 UTC (rev 17198)
+++
jpa-api/trunk/src/main/java/javax/persistence/spi/PersistenceProviderResolverHolder.java 2009-07-23
21:25:08 UTC (rev 17199)
@@ -2,23 +2,166 @@
// EJB3 Specification Copyright 2004-2009 Sun Microsystems, Inc.
package javax.persistence.spi;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.persistence.PersistenceException;
+
/**
* Holds the global PersistenceProviderResolver instance.
* If no PersistenceProviderResolver is set by the environment,
- * the default PersistenceProviderResolver is used. *
+ * the default PersistenceProviderResolver is used.
+ *
* Implementations must be thread-safe.
*/
public class PersistenceProviderResolverHolder {
+
+ private static volatile PersistenceProviderResolver resolver;
+
+ private static final PersistenceProviderResolver defaultResolver = new
PersistenceProviderResolverPerClassLoader();
+
/**
* Returns the current persistence provider resolver
*/
public static PersistenceProviderResolver getPersistenceProviderResolver() {
- return null;
+ return resolver == null ? defaultResolver : resolver;
}
/**
* Defines the persistence provider resolver used
*/
public static void setPersistenceProviderResolver(PersistenceProviderResolver resolver)
{
+ PersistenceProviderResolverHolder.resolver = resolver;
}
+
+ /**
+ * Cache PersistenceProviderResolver per classloader and use the current classloader as
a
+ * key.
+ * Use CachingPersistenceProviderResolver for each PersistenceProviderResolver
instance.
+ *
+ * @author Emmanuel Bernard
+ */
+ private static class PersistenceProviderResolverPerClassLoader implements
PersistenceProviderResolver {
+
+ //FIXME use a ConcurrentHashMap with weak entry
+ private final WeakHashMap<ClassLoader, PersistenceProviderResolver> resolvers =
+ new WeakHashMap<ClassLoader, PersistenceProviderResolver>();
+ private volatile short barrier = 1;
+
+
+ public List<PersistenceProvider> getPersistenceProviders() {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if ( cl == null ) {
+ cl = PersistenceProviderResolverPerClassLoader.class.getClassLoader();
+ }
+ if (barrier == 1) {} //read barrier syncs state with other threads
+ PersistenceProviderResolver currentResolver = resolvers.get( cl );
+ if (currentResolver == null) {
+ currentResolver = new CachingPersistenceProviderResolver(cl);
+ resolvers.put( cl, currentResolver );
+ barrier = 1;
+ }
+ return currentResolver.getPersistenceProviders();
+ }
+
+ /**
+ * Resolve the list of Persistence providers for a given classloader and cache the
results.
+ *
+ * Avoids to keep any reference from this class to the classloader being
+ * passed to the constructor.
+ *
+ * @author Emmanuel Bernard
+ */
+ private static class CachingPersistenceProviderResolver implements
PersistenceProviderResolver {
+ //this assumes that the class loader keeps the list of classes loaded
+ private final List<WeakReference<Class<? extends
PersistenceProvider>>> resolverClasses;
+
+ public CachingPersistenceProviderResolver(ClassLoader cl) {
+ //creates a hard reference to the cl :(
+ resolverClasses = new ArrayList<WeakReference<Class<? extends
PersistenceProvider>>>();
+ try {
+ Enumeration<URL> resources = cl.getResources( "META-INF/services/" +
PersistenceProvider.class.getName() );
+ Set<String> names = new HashSet<String>();
+ while ( resources.hasMoreElements() ) {
+ URL url = resources.nextElement();
+ InputStream is = url.openStream();
+ try {
+ names.addAll( providerNamesFromReader( new BufferedReader( new InputStreamReader(
is ) ) ) );
+ }
+ finally {
+ is.close();
+ }
+ }
+ for ( String s : names ) {
+ @SuppressWarnings( "unchecked" )
+ Class<? extends PersistenceProvider> providerClass = (Class<? extends
PersistenceProvider>) cl.loadClass( s );
+ WeakReference<Class<? extends PersistenceProvider>> reference
+ = new WeakReference<Class<? extends
PersistenceProvider>>(providerClass);
+ //keep Hibernate atop
+ if ( s.endsWith( "HibernatePersistence" ) &&
resolverClasses.size() > 0 ) {
+ WeakReference<Class<? extends PersistenceProvider>> movedReference =
resolverClasses.get( 0 );
+ resolverClasses.add( 0, reference );
+ resolverClasses.add( movedReference );
+ }
+ else {
+ resolverClasses.add( reference );
+ }
+ }
+ }
+ catch ( IOException e ) {
+ throw new PersistenceException( e );
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new PersistenceException( e );
+ }
+ }
+
+ //TODO find a way to cache property instances
+ //problem #1: avoid hard ref with classloader (List<WR<PP>>?
+ //problem #2: avoid half GC lists
+ public List<PersistenceProvider> getPersistenceProviders() {
+ List<PersistenceProvider> providers = new ArrayList<PersistenceProvider>(
resolverClasses.size() );
+ try {
+ for ( WeakReference<Class<? extends PersistenceProvider>> providerClass
: resolverClasses ) {
+ providers.add( providerClass.get().newInstance() );
+ }
+ }
+ catch ( InstantiationException e ) {
+ throw new PersistenceException( e );
+ }
+ catch ( IllegalAccessException e ) {
+ throw new PersistenceException( e );
+ }
+ return providers;
+ }
+
+
+ private static final Pattern nonCommentPattern = Pattern.compile( "^([^#]+)"
);
+
+ private static Set<String> providerNamesFromReader(BufferedReader reader) throws
IOException {
+ Set<String> names = new HashSet<String>();
+ String line;
+ while ( ( line = reader.readLine() ) != null ) {
+ line = line.trim();
+ Matcher m = nonCommentPattern.matcher( line );
+ if ( m.find() ) {
+ names.add( m.group().trim() );
+ }
+ }
+ return names;
+ }
+ }
+ }
+
}