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

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

Go to download

The Java Persistence API (JPA) : a standard interface-based Java model abstraction of persistence, developed by the JCP.

There is a newer version: 2.2.4
Show newest version
/*
 * Copyright (c) 2008, 2009, 2011 Oracle, Inc. 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.
 */
package javax.persistence.spi;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 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 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 service loading mechanism as
     * described in the Java Persistence specification. A ServiceLoader.load() call is made with the current
     * context classloader to find the service provider files on the classpath.
     */
    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 Map 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)
            {
                loadedProviders = new ArrayList<>();
                Iterator ipp = ServiceLoader.load(PersistenceProvider.class, loader).iterator();
                try
                {
                    while (ipp.hasNext())
                    {
                        try
                        {
                            PersistenceProvider pp = ipp.next();
                            loadedProviders.add(pp);
                        }
                        catch (ServiceConfigurationError sce)
                        {
                            log(Level.FINEST, sce.toString());
                        }
                    }
                }
                catch (ServiceConfigurationError sce)
                {
                    log(Level.FINEST, sce.toString());
                }

                // If none are found we'll log the provider names for diagnostic
                // purposes.
                if (loadedProviders.isEmpty())
                {
                }

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

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

        /**
         * 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 - 2025 Weber Informatics LLC | Privacy Policy