All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.persistence.spi.PersistenceProviderResolverHolder Maven / Gradle / Ivy

Go to download

Hibernate developmental JSR 317 (Java Persistence API 2.0) contracts. Used to allow incremental implementation of features on the way to full JPA 2.0 support.

The newest version!
// $Id: PersistenceProviderResolverHolder.java 17201 2009-07-25 05:24:55Z epbernard $
// 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.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
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.
 *
 * 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 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 resolvers =
				new WeakHashMap();
		private volatile short barrier = 1;
		

		public List 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>> resolverClasses;

			public CachingPersistenceProviderResolver(ClassLoader cl) {
				resolverClasses = new ArrayList>>();
				try {
					Enumeration resources = cl.getResources( "META-INF/services/" + PersistenceProvider.class.getName() );
					Set names = new HashSet();
					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 providerClass = (Class) cl.loadClass( s );
						WeakReference> reference
								= new WeakReference>(providerClass);
						//keep Hibernate atop
						if ( s.endsWith( "HibernatePersistence" ) && resolverClasses.size() > 0 ) {
							WeakReference> 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>?
			//problem #2: avoid half GC lists
			public List getPersistenceProviders() {
				List providers = new ArrayList( resolverClasses.size() );
				try {
					for ( WeakReference> 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 providerNamesFromReader(BufferedReader reader) throws IOException {
				Set names = new HashSet();
				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;
			}
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy