Skip to content

Package: DefaultPersistenceProviderResolver

DefaultPersistenceProviderResolver

nameinstructionbranchcomplexitylinemethod
DefaultPersistenceProviderResolver()
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
clearCachedProviders()
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
getContextClassLoader()
M: 4 C: 5
56%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 2
67%
M: 0 C: 1
100%
getPersistenceProviders()
M: 5 C: 72
94%
M: 0 C: 6
100%
M: 0 C: 4
100%
M: 2 C: 20
91%
M: 0 C: 1
100%
lambda$getContextClassLoader$0()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
processQueue()
M: 7 C: 7
50%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 2
67%
M: 0 C: 1
100%
static {...}
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%

Coverage

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