Skip to content

Method: getContextClassLoader()

1: /*
2: * JOPA
3: * Copyright (C) 2024 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.jopa.model;
19:
20: import org.slf4j.Logger;
21: import org.slf4j.LoggerFactory;
22:
23: import java.lang.ref.ReferenceQueue;
24: import java.lang.ref.SoftReference;
25: import java.lang.ref.WeakReference;
26: import java.security.AccessController;
27: import java.security.PrivilegedAction;
28: import java.util.ArrayList;
29: import java.util.HashMap;
30: import java.util.Iterator;
31: import java.util.List;
32: import java.util.Optional;
33: import java.util.ServiceConfigurationError;
34: import java.util.ServiceLoader;
35:
36: /**
37: * Default implementation of the {@link PersistenceProviderResolver}, threadsafe.
38: * <p>
39: * Uses service loading mechanism ({@link ServiceLoader#load(Class, ClassLoader)} using the current context thread's class loader) to discover {@link PersistenceProvider} implementations on the classpath.
40: * <p>
41: * Code based on JPA specification implementation.
42: */
43: public class DefaultPersistenceProviderResolver implements PersistenceProviderResolver {
44:
45: private static final Logger LOG = LoggerFactory.getLogger(DefaultPersistenceProviderResolver.class);
46:
47: /**
48: * Queue for reference objects referring to class loaders or persistence providers.
49: */
50: private static final ReferenceQueue<?> referenceQueue = new ReferenceQueue<>();
51:
52: /**
53: * Available providers cached by CacheKey to ensure there is no potential for provider visibility issues.
54: */
55: private final HashMap<CacheKey, PersistenceProviderReference> providers = new HashMap<>();
56:
57: public List<PersistenceProvider> getPersistenceProviders() {
58: // Before we do the real loading work, see whether we need to
59: // do some cleanup: If references to class loaders or
60: // persistence providers have been nulled out, remove all related
61: // information from the cache.
62: processQueue();
63:
64: final ClassLoader loader = getContextClassLoader();
65: final CacheKey cacheKey = new CacheKey(loader);
66: PersistenceProviderReference providersReferent = providers.get(cacheKey);
67:
68: if (providersReferent != null) {
69: return providersReferent.get();
70: }
71:
72: List<PersistenceProvider> loadedProviders = new ArrayList<>();
73: Iterator<PersistenceProvider> ipp = ServiceLoader.load(PersistenceProvider.class, loader).iterator();
74: try {
75: loadProvider(ipp).ifPresent(loadedProviders::add);
76: } catch (ServiceConfigurationError sce) {
77: LOG.warn("Unable to load PersistenceProvider implementation via service loader.", sce);
78: }
79:
80: if (loadedProviders.isEmpty()) {
81: LOG.warn("No valid providers found.");
82: }
83:
84: providersReferent = new PersistenceProviderReference(loadedProviders, referenceQueue, cacheKey);
85:
86: providers.put(cacheKey, providersReferent);
87:
88: return loadedProviders;
89: }
90:
91: private static Optional<PersistenceProvider> loadProvider(Iterator<PersistenceProvider> ipp) {
92: try {
93: return Optional.of(ipp.next());
94: } catch (ServiceConfigurationError sce) {
95: LOG.warn("Unable to load PersistenceProvider implementation via service loader.", sce);
96: return Optional.empty();
97: }
98: }
99:
100: /**
101: * Remove garbage collected cache keys & providers.
102: */
103: private void processQueue() {
104: CacheKeyReference ref;
105: while ((ref = (CacheKeyReference) referenceQueue.poll()) != null) {
106: providers.remove(ref.getCacheKey());
107: }
108: }
109:
110: /**
111: * Wraps <code>Thread.currentThread().getContextClassLoader()</code> into a doPrivileged block if security manager is present
112: */
113: private static ClassLoader getContextClassLoader() {
114:• if (System.getSecurityManager() == null) {
115: return Thread.currentThread().getContextClassLoader();
116: } else {
117: return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () -> Thread.currentThread()
118: .getContextClassLoader());
119: }
120: }
121:
122: /**
123: * Clear all cached providers
124: */
125: public void clearCachedProviders() {
126: providers.clear();
127: }
128:
129:
130: /**
131: * The common interface to get a CacheKey implemented by
132: * LoaderReference and PersistenceProviderReference.
133: */
134: private interface CacheKeyReference {
135: CacheKey getCacheKey();
136: }
137:
138: /**
139: * Key used for cached persistence providers. The key checks
140: * the class loader to determine if the persistence providers
141: * is a match to the requested one. The loader may be null.
142: */
143: private class CacheKey implements Cloneable {
144:
145: /* Weak Reference to ClassLoader */
146: private LoaderReference loaderRef;
147:
148: /* Cached Hashcode */
149: private int hashCodeCache;
150:
151: CacheKey(ClassLoader loader) {
152: if (loader == null) {
153: this.loaderRef = null;
154: } else {
155: loaderRef = new LoaderReference(loader, referenceQueue, this);
156: }
157: calculateHashCode();
158: }
159:
160: ClassLoader getLoader() {
161: return (loaderRef != null) ? loaderRef.get() : null;
162: }
163:
164: @Override
165: public boolean equals(Object other) {
166: if (this == other) {
167: return true;
168: }
169: if (other == null || !getClass().equals(other.getClass())) {
170: return false;
171: }
172: final CacheKey otherEntry = (CacheKey) other;
173: // quick check to see if they are not equal
174: if (hashCodeCache != otherEntry.hashCodeCache) {
175: return false;
176: }
177: // are refs (both non-null) or (both null)?
178: if (loaderRef == null) {
179: return otherEntry.loaderRef == null;
180: }
181: ClassLoader loader = loaderRef.get();
182: // With a null reference we can no longer find out which class loader was referenced; so treat it as unequal
183: return (otherEntry.loaderRef != null) && (loader != null) && (loader == otherEntry.loaderRef.get());
184: }
185:
186: @Override
187: public int hashCode() {
188: return hashCodeCache;
189: }
190:
191: private void calculateHashCode() {
192: ClassLoader loader = getLoader();
193: if (loader != null) {
194: hashCodeCache = loader.hashCode();
195: }
196: }
197:
198: public Object clone() {
199: try {
200: CacheKey clone = (CacheKey) super.clone();
201: if (loaderRef != null) {
202: clone.loaderRef = new LoaderReference(loaderRef.get(), referenceQueue, clone);
203: }
204: return clone;
205: } catch (CloneNotSupportedException e) {
206: // this should never happen
207: throw new InternalError(e);
208: }
209: }
210:
211: public String toString() {
212: return "CacheKey[" + getLoader() + ")]";
213: }
214: }
215:
216: /**
217: * References to class loaders are weak references, so that they can be
218: * garbage collected when nobody else is using them. The DefaultPersistenceProviderResolver
219: * class has no reason to keep class loaders alive.
220: */
221: private class LoaderReference extends WeakReference<ClassLoader> implements CacheKeyReference {
222: private final CacheKey cacheKey;
223:
224: @SuppressWarnings("unchecked")
225: LoaderReference(ClassLoader referent, ReferenceQueue q, CacheKey key) {
226: super(referent, q);
227: cacheKey = key;
228: }
229:
230: public CacheKey getCacheKey() {
231: return cacheKey;
232: }
233: }
234:
235: /**
236: * References to persistence provider are soft references so that they can be garbage
237: * collected when they have no hard references.
238: */
239: private class PersistenceProviderReference extends SoftReference<List<PersistenceProvider>> implements CacheKeyReference {
240: private final CacheKey cacheKey;
241:
242: @SuppressWarnings("unchecked")
243: PersistenceProviderReference(List<PersistenceProvider> referent, ReferenceQueue q, CacheKey key) {
244: super(referent, q);
245: cacheKey = key;
246: }
247:
248: public CacheKey getCacheKey() {
249: return cacheKey;
250: }
251: }
252: }