1 /*
2 * Copyright (c) 2008, 2019 Oracle and/or its affiliates. All rights reserved.
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-2.0,
7 * or the Eclipse Distribution License v. 1.0 which is available at
8 * http://www.eclipse.org/org/documents/edl-v10.php.
9 *
10 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11 */
12
13 // Contributors:
14 // Lukas Jungmann - 2.2
15 // Linda DeMichiel - 2.1
16 // Linda DeMichiel - 2.0
17
18 package javax.persistence.spi;
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.ArrayList;
26 import java.util.List;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.ServiceConfigurationError;
30 import java.util.ServiceLoader;
31 import java.util.logging.Level;
32 import java.util.logging.Logger;
33
34
35 /**
36 * Holds the global {@link javax.persistence.spi.PersistenceProviderResolver}
37 * instance. If no <code>PersistenceProviderResolver</code> is set by the
38 * environment, the default <code>PersistenceProviderResolver</code> is used.
39 *
40 * Implementations must be thread-safe.
41 *
42 * @since 2.0
43 */
44 public class PersistenceProviderResolverHolder {
45
46 private static PersistenceProviderResolver singleton = new DefaultPersistenceProviderResolver();
47
48 /**
49 * Returns the current persistence provider resolver.
50 *
51 * @return the current persistence provider resolver
52 */
53 public static PersistenceProviderResolver getPersistenceProviderResolver() {
54 return singleton;
55 }
56
57 /**
58 * Defines the persistence provider resolver used.
59 *
60 * @param resolver persistence provider resolver to be used.
61 */
62 public static void setPersistenceProviderResolver(PersistenceProviderResolver resolver) {
63 if (resolver == null) {
64 singleton = new DefaultPersistenceProviderResolver();
65 } else {
66 singleton = resolver;
67 }
68 }
69
70 /**
71 * Default provider resolver class to use when none is explicitly set.
72 *
73 * Uses service loading mechanism as described in the Jakarta Persistence
74 * specification. A ServiceLoader.load() call is made with the current context
75 * classloader to find the service provider files on the classpath.
76 */
77 private static class DefaultPersistenceProviderResolver implements PersistenceProviderResolver {
78
79 /**
80 * Cached list of available providers cached by CacheKey to ensure
81 * there is not potential for provider visibility issues.
82 */
83 private volatile HashMap<CacheKey, PersistenceProviderReference> providers = new HashMap<CacheKey, PersistenceProviderReference>();
84
85 /**
86 * Queue for reference objects referring to class loaders or persistence providers.
87 */
88 private static final ReferenceQueue referenceQueue = new ReferenceQueue();
89
90 public List<PersistenceProvider> getPersistenceProviders() {
91 // Before we do the real loading work, see whether we need to
92 // do some cleanup: If references to class loaders or
93 // persistence providers have been nulled out, remove all related
94 // information from the cache.
95 processQueue();
96
97 ClassLoader loader = getContextClassLoader();
98 CacheKey cacheKey = new CacheKey(loader);
99 PersistenceProviderReference providersReferent = this.providers.get(cacheKey);
100 List<PersistenceProvider> loadedProviders = null;
101
102 if (providersReferent != null) {
103 loadedProviders = providersReferent.get();
104 }
105
106 if (loadedProviders == null) {
107 loadedProviders = new ArrayList<>();
108 Iterator<PersistenceProvider> ipp = ServiceLoader.load(PersistenceProvider.class, loader).iterator();
109 try {
110 while (ipp.hasNext()) {
111 try {
112 PersistenceProvider pp = ipp.next();
113 loadedProviders.add(pp);
114 } catch (ServiceConfigurationError sce) {
115 log(Level.FINEST, sce.toString());
116 }
117 }
118 } catch (ServiceConfigurationError sce) {
119 log(Level.FINEST, sce.toString());
120 }
121
122 // If none are found we'll log the provider names for diagnostic
123 // purposes.
124 if (loadedProviders.isEmpty()) {
125 log(Level.WARNING, "No valid providers found.");
126 }
127
128 providersReferent = new PersistenceProviderReference(loadedProviders, referenceQueue, cacheKey);
129
130 this.providers.put(cacheKey, providersReferent);
131 }
132
133 return loadedProviders;
134 }
135
136 /**
137 * Remove garbage collected cache keys & providers.
138 */
139 private void processQueue() {
140 CacheKeyReference ref;
141 while ((ref = (CacheKeyReference) referenceQueue.poll()) != null) {
142 providers.remove(ref.getCacheKey());
143 }
144 }
145
146 /**
147 * Wraps <code>Thread.currentThread().getContextClassLoader()</code> into a doPrivileged block if security manager is present
148 */
149 private static ClassLoader getContextClassLoader() {
150 if (System.getSecurityManager() == null) {
151 return Thread.currentThread().getContextClassLoader();
152 } else {
153 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
154 public ClassLoader run() {
155 return Thread.currentThread().getContextClassLoader();
156 }
157 });
158 }
159 }
160
161
162 private static final String LOGGER_SUBSYSTEM = "javax.persistence.spi";
163
164 private Logger logger;
165
166 private void log(Level level, String message) {
167 if (this.logger == null) {
168 this.logger = Logger.getLogger(LOGGER_SUBSYSTEM);
169 }
170 this.logger.log(level, LOGGER_SUBSYSTEM + "::" + message);
171 }
172
173 /**
174 * Clear all cached providers
175 */
176 public void clearCachedProviders() {
177 this.providers.clear();
178 }
179
180
181 /**
182 * The common interface to get a CacheKey implemented by
183 * LoaderReference and PersistenceProviderReference.
184 */
185 private interface CacheKeyReference {
186 public CacheKey getCacheKey();
187 }
188
189 /**
190 * Key used for cached persistence providers. The key checks
191 * the class loader to determine if the persistence providers
192 * is a match to the requested one. The loader may be null.
193 */
194 private class CacheKey implements Cloneable {
195
196 /* Weak Reference to ClassLoader */
197 private LoaderReference loaderRef;
198
199 /* Cached Hashcode */
200 private int hashCodeCache;
201
202 CacheKey(ClassLoader loader) {
203 if (loader == null) {
204 this.loaderRef = null;
205 } else {
206 loaderRef = new LoaderReference(loader, referenceQueue, this);
207 }
208 calculateHashCode();
209 }
210
211 ClassLoader getLoader() {
212 return (loaderRef != null) ? loaderRef.get() : null;
213 }
214
215 public boolean equals(Object other) {
216 if (this == other) {
217 return true;
218 }
219 try {
220 final CacheKey otherEntry = (CacheKey) other;
221 // quick check to see if they are not equal
222 if (hashCodeCache != otherEntry.hashCodeCache) {
223 return false;
224 }
225 // are refs (both non-null) or (both null)?
226 if (loaderRef == null) {
227 return otherEntry.loaderRef == null;
228 }
229 ClassLoader loader = loaderRef.get();
230 return (otherEntry.loaderRef != null)
231 // with a null reference we can no longer find
232 // out which class loader was referenced; so
233 // treat it as unequal
234 && (loader != null) && (loader == otherEntry.loaderRef.get());
235 } catch (NullPointerException e) {
236 } catch (ClassCastException e) {
237 }
238
239 return false;
240 }
241
242 public int hashCode() {
243 return hashCodeCache;
244 }
245
246 private void calculateHashCode() {
247 ClassLoader loader = getLoader();
248 if (loader != null) {
249 hashCodeCache = loader.hashCode();
250 }
251 }
252
253 public Object clone() {
254 try {
255 CacheKey clone = (CacheKey) super.clone();
256 if (loaderRef != null) {
257 clone.loaderRef = new LoaderReference(loaderRef.get(), referenceQueue, clone);
258 }
259 return clone;
260 } catch (CloneNotSupportedException e) {
261 // this should never happen
262 throw new InternalError();
263 }
264 }
265
266 public String toString() {
267 return "CacheKey[" + getLoader() + ")]";
268 }
269 }
270
271 /**
272 * References to class loaders are weak references, so that they can be
273 * garbage collected when nobody else is using them. The DefaultPersistenceProviderResolver
274 * class has no reason to keep class loaders alive.
275 */
276 private class LoaderReference extends WeakReference<ClassLoader>
277 implements CacheKeyReference {
278 private CacheKey cacheKey;
279
280 @SuppressWarnings("unchecked")
281 LoaderReference(ClassLoader referent, ReferenceQueue q, CacheKey key) {
282 super(referent, q);
283 cacheKey = key;
284 }
285
286 public CacheKey getCacheKey() {
287 return cacheKey;
288 }
289 }
290
291 /**
292 * References to persistence provider are soft references so that they can be garbage
293 * collected when they have no hard references.
294 */
295 private class PersistenceProviderReference extends SoftReference<List<PersistenceProvider>>
296 implements CacheKeyReference {
297 private CacheKey cacheKey;
298
299 @SuppressWarnings("unchecked")
300 PersistenceProviderReference(List<PersistenceProvider> referent, ReferenceQueue q, CacheKey key) {
301 super(referent, q);
302 cacheKey = key;
303 }
304
305 public CacheKey getCacheKey() {
306 return cacheKey;
307 }
308 }
309 }
310 }
311