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

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

There is a newer version: 2.2.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2008, 2009 Sun Microsystems. All rights reserved. 
 * 
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
 * which accompanies this distribution. 
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * 
 * Contributors:
 *     Linda DeMichiel - Java Persistence 2.0 - Version 2.0 (October 1, 2009)
 *     Specification available from http://jcp.org/en/jsr/detail?id=317
 *     dclarke - EclipseLink implementation and default resolver
 *
 ******************************************************************************/
package javax.persistence.spi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.PersistenceException;

/**
 * Holds the global {@link javax.persistence.spi.PersistenceProviderResolver}
 * instance. If no PersistenceProviderResolver is set by the
 * environment, the default PersistenceProviderResolver is used.
 * 
 * Implementations must be thread-safe.
 * 
 * @since Java Persistence 2.0
 */
public class PersistenceProviderResolverHolder {

    private static PersistenceProviderResolver singleton = new DefaultPersistenceProviderResolver();

    /**
     * Returns the current persistence provider resolver.
     * 
     * @return the current persistence provider resolver
     */
    public static PersistenceProviderResolver getPersistenceProviderResolver() {
        return singleton;
    }

    /**
     * Defines the persistence provider resolver used.
     * 
     * @param resolver persistence provider resolver to be used.
     */
    public static void setPersistenceProviderResolver(PersistenceProviderResolver resolver) {
        if (resolver == null) {
            singleton = new DefaultPersistenceProviderResolver();
        } else {
            singleton = resolver;
        }
    }

    /**
     * Default provider resolver class to use when none is explicitly set.
     * 
     * Uses the META-INF/services approach as described in the Java Persistence
     * specification. A getResources() call is made on the current context
     * classloader to find the service provider files on the classpath. Any
     * service files found are then read to obtain the classes that implement
     * the persistence provider interface.
     */
    private static class DefaultPersistenceProviderResolver implements PersistenceProviderResolver {

        /**
         * Cached list of available providers cached by CacheKey to ensure
         * there is not potential for provider visibility issues. 
         */
        private volatile HashMap providers = new HashMap();
        
        /**
         * Queue for reference objects referring to class loaders or persistence providers.
         */
        private static final ReferenceQueue referenceQueue = new ReferenceQueue();

        public List getPersistenceProviders() {
            // Before we do the real loading work, see whether we need to
            // do some cleanup: If references to class loaders or
            // persistence providers have been nulled out, remove all related
            // information from the cache.
            processQueue();
            
            ClassLoader loader = getContextClassLoader();
            CacheKey cacheKey = new CacheKey(loader);
            PersistenceProviderReference providersReferent = this.providers.get(cacheKey);
            List loadedProviders = null;
            
            if (providersReferent != null) {
                loadedProviders = providersReferent.get();
            }

            if (loadedProviders == null) {
                Collection providerNames = getProviderNames(loader);
                loadedProviders = new ArrayList();

                for (ProviderName providerName : providerNames) {
                    try {
                        PersistenceProvider provider = (PersistenceProvider) loader.loadClass(providerName.getName()).newInstance();
                        loadedProviders.add(provider);
                    } catch (ClassNotFoundException cnfe) {
                        log(Level.FINEST, cnfe + ": " + providerName);
                    } catch (InstantiationException ie) {
                        log(Level.FINEST, ie + ": " + providerName);
                    } catch (IllegalAccessException iae) {
                        log(Level.FINEST, iae + ": " + providerName);
                    } catch (ClassCastException cce) {
                        log(Level.FINEST, cce + ": " + providerName);
                    }
                }

                // If none are found we'll log the provider names for diagnostic
                // purposes.
                if (loadedProviders.isEmpty() && !providerNames.isEmpty()) {
                    log(Level.WARNING, "No valid providers found using:");
                    for (ProviderName name : providerNames) {
                        log(Level.WARNING, name.toString());
                    }
                }
                
                providersReferent = new PersistenceProviderReference(loadedProviders, referenceQueue, cacheKey);

                this.providers.put(cacheKey, providersReferent);
            }

            return loadedProviders;
        }
        
        /**
         * Remove garbage collected cache keys & providers.
         */
        private void processQueue() {
            CacheKeyReference ref;
            while ((ref = (CacheKeyReference) referenceQueue.poll()) != null) {
                providers.remove(ref.getCacheKey());
            }            
        }

        /**
         * Wraps Thread.currentThread().getContextClassLoader() into a doPrivileged block if security manager is present
         */
        private static ClassLoader getContextClassLoader() {
            if (System.getSecurityManager() == null) {
                return Thread.currentThread().getContextClassLoader();
            }
            else {
                return  (ClassLoader) java.security.AccessController.doPrivileged(
                        new java.security.PrivilegedAction() {
                            public java.lang.Object run() {
                                return Thread.currentThread().getContextClassLoader();
                            }
                        }
                );
            }
        }


        private static final String LOGGER_SUBSYSTEM = "javax.persistence.spi";

        private Logger logger;

        private void log(Level level, String message) {
            if (this.logger == null) {
                this.logger = Logger.getLogger(LOGGER_SUBSYSTEM);
            }
            this.logger.log(level, LOGGER_SUBSYSTEM + "::" + message);
        }

        private static final String SERVICE_PROVIDER_FILE = "META-INF/services/javax.persistence.spi.PersistenceProvider";

        /**
         * Locate all JPA provider services files and collect all of the
         * provider names available.
         */
        private Collection getProviderNames(ClassLoader loader) {
            Enumeration resources = null;

            try {
                resources = loader.getResources(SERVICE_PROVIDER_FILE);
            } catch (IOException ioe) {
                throw new PersistenceException("IOException caught: " + loader + ".getResources(" + SERVICE_PROVIDER_FILE + ")", ioe);
            }

            Collection providerNames = new ArrayList();

            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                addProviderNames(url, providerNames);
            }

            return providerNames;
        }

        private static final Pattern nonCommentPattern = Pattern.compile("^([^#]+)");

        /**
         * For each services file look for uncommented provider names on each
         * line.
         */
        private void addProviderNames(URL url, Collection providerNames) {
            InputStream in = null;
            try {
                in = url.openStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));

                String line;
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    Matcher m = nonCommentPattern.matcher(line);
                    if (m.find()) {
                        providerNames.add(new ProviderName(m.group().trim(), url));
                    }
                }
            } catch (IOException ioe) {
                throw new PersistenceException("IOException caught reading: " + url, ioe);
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                    }
                }
            }
        }

        /**
         * Clear all cached providers
         */
        public void clearCachedProviders() {
            this.providers.clear();
        }

        /**
         * A ProviderName captures each provider name found in a service file as
         * well as the URL for the service file it was found in. This
         * information is only used for diagnostic purposes.
         */
        private class ProviderName {

            /** Provider name **/
            private String name;

            /** URL for the service file where the provider name was found **/
            private URL source;

            public ProviderName(String name, URL sourceURL) {
                this.name = name;
                this.source = sourceURL;
            }

            public String getName() {
                return name;
            }

            public URL getSource() {
                return source;
            }

            public String toString() {
                return getName() + " - " + getSource();
            }
        }
        
        /**
         * The common interface to get a CacheKey implemented by
         * LoaderReference and PersistenceProviderReference.
         */
        private interface CacheKeyReference {
            public CacheKey getCacheKey();
        }        
        
        /**
          * Key used for cached persistence providers. The key checks
          * the class loader to determine if the persistence providers
          * is a match to the requested one. The loader may be null.
          */
        private class CacheKey implements Cloneable {
            
            /* Weak Reference to ClassLoader */
            private LoaderReference loaderRef;
            
            /* Cached Hashcode */
            private int hashCodeCache;

            CacheKey(ClassLoader loader) {
                if (loader == null) {
                    this.loaderRef = null;
                } else {
                    loaderRef = new LoaderReference(loader, referenceQueue, this);
                }
                calculateHashCode();
            }

            ClassLoader getLoader() {
                return (loaderRef != null) ? loaderRef.get() : null;
            }

            public boolean equals(Object other) {
                if (this == other) {
                    return true;
                }
                try {
                    final CacheKey otherEntry = (CacheKey) other;
                    // quick check to see if they are not equal
                    if (hashCodeCache != otherEntry.hashCodeCache) {
                        return false;
                    }
                    // are refs (both non-null) or (both null)?
                    if (loaderRef == null) {
                        return otherEntry.loaderRef == null;
                    }
                    ClassLoader loader = loaderRef.get();
                    return (otherEntry.loaderRef != null)
                    // with a null reference we can no longer find
                    // out which class loader was referenced; so
                    // treat it as unequal
                    && (loader != null) && (loader == otherEntry.loaderRef.get());
                } catch (NullPointerException e) {
                } catch (ClassCastException e) {
                }

                return false;
            }

            public int hashCode() {
                return hashCodeCache;
            }

            private void calculateHashCode() {
                ClassLoader loader = getLoader();
                if (loader != null) {
                    hashCodeCache = loader.hashCode();
                }
            }

            public Object clone() {
                try {
                    CacheKey clone = (CacheKey) super.clone();
                    if (loaderRef != null) {
                        clone.loaderRef = new LoaderReference(loaderRef.get(), referenceQueue, clone);
                    }
                    return clone;
                } catch (CloneNotSupportedException e) {
                    // this should never happen
                    throw new InternalError();
                }
            }

            public String toString() {
                return "CacheKey[" + getLoader() + ")]";
            }
        }
       
       /**
         * References to class loaders are weak references, so that they can be
         * garbage collected when nobody else is using them. The DefaultPersistenceProviderResolver 
         * class has no reason to keep class loaders alive.
         */
        private class LoaderReference extends WeakReference 
                implements CacheKeyReference {
            private CacheKey cacheKey;

            LoaderReference(ClassLoader referent, ReferenceQueue q, CacheKey key) {
                super(referent, q);
                cacheKey = key;
            }

            public CacheKey getCacheKey() {
                return cacheKey;
            }
        }

        /**
         * References to persistence provider are soft references so that they can be garbage
         * collected when they have no hard references.
         */
        private class PersistenceProviderReference extends SoftReference>
                implements CacheKeyReference {
            private CacheKey cacheKey;

            PersistenceProviderReference(List referent, ReferenceQueue q, CacheKey key) {
                super(referent, q);
                cacheKey = key;
            }

            public CacheKey getCacheKey() {
                return cacheKey;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy