/*******************************************************************************
* 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 <code>PersistenceProviderResolver</code> is set by the
* environment, the default <code>PersistenceProviderResolver</code> 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<
CacheKey,
PersistenceProviderReference>
providers = new
HashMap<
CacheKey,
PersistenceProviderReference>();
/**
* Queue for reference objects referring to class loaders or persistence providers.
*/
private static final
ReferenceQueue referenceQueue = new
ReferenceQueue();
public
List<
PersistenceProvider>
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<
PersistenceProvider>
loadedProviders = null;
if (
providersReferent != null) {
loadedProviders =
providersReferent.
get();
}
if (
loadedProviders == null) {
loadedProviders = new
ArrayList<>();
Iterator<
PersistenceProvider>
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 <code>Thread.currentThread().getContextClassLoader()</code> 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<
ClassLoader>() {
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<
ClassLoader>
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<
List<
PersistenceProvider>>
implements
CacheKeyReference {
private
CacheKey cacheKey;
@
SuppressWarnings("unchecked")
PersistenceProviderReference(
List<
PersistenceProvider>
referent,
ReferenceQueue q,
CacheKey key) {
super(
referent,
q);
cacheKey =
key;
}
public
CacheKey getCacheKey() {
return
cacheKey;
}
}
}
}