[hibernate-commits] Hibernate SVN: r17199 - jpa-api/trunk/src/main/java/javax/persistence/spi.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Jul 23 17:25:08 EDT 2009


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;
+			}
+		}
+	}
+
 }



More information about the hibernate-commits mailing list