javax.persistence.spi.PersistenceProviderResolverHolder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javax.persistence Show documentation
Show all versions of javax.persistence Show documentation
javax.persistence build based upon git transaction cfcdce1
/*******************************************************************************
* Copyright (c) 2008 - 2017 Oracle Corporation. 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:
* Lukas Jungmann - Java Persistence 2.2
* Linda DeMichiel - Java Persistence 2.1
* Linda DeMichiel - Java Persistence 2.0
*
******************************************************************************/
package javax.persistence.spi;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 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 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 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) {
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()) {
log(Level.WARNING, "No valid providers found.");
}
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 AccessController.doPrivileged(new PrivilegedAction() {
public ClassLoader 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;
@SuppressWarnings("unchecked")
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;
@SuppressWarnings("unchecked")
PersistenceProviderReference(List referent, ReferenceQueue q, CacheKey key) {
super(referent, q);
cacheKey = key;
}
public CacheKey getCacheKey() {
return cacheKey;
}
}
}
}