1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.jexl3.internal;
18
19 import java.lang.ref.Reference;
20 import java.lang.ref.ReferenceQueue;
21 import java.lang.ref.WeakReference;
22 import java.util.HashSet;
23 import java.util.Set;
24 import java.util.function.IntFunction;
25
26 import org.apache.commons.jexl3.JexlCache;
27
28 /**
29 * A meta-cache that tracks multiple JexlCache instances via weak references.
30 * <p>
31 * Each JexlCache created by this MetaCache is held via a WeakReference,
32 * allowing it to be garbage collected as soon as no strong references exist.
33 * </p>
34 * <p>
35 * This allows for collective management of multiple caches, in particular clearing all caches at once.
36 * This operation is typically called when the uberspect class loader needs to change.
37 * </p>
38 */
39 final class MetaCache {
40 // The factory to create new JexlCache instances
41 private final IntFunction<JexlCache<?, ?>> factory;
42 // The set of JexlCache references
43 private final Set<Reference<JexlCache<?, ?>>> references;
44 // Queue to receive references whose referent has been garbage collected
45 private final ReferenceQueue<JexlCache<?, ?>> queue;
46
47 /**
48 * Constructs a MetaCache with the given cache factory.
49 *
50 * @param factory The factory function to create JexlCache instances given a capacity.
51 */
52 MetaCache(final IntFunction<JexlCache<?, ?>> factory) {
53 this.factory = factory;
54 this.references = new HashSet<>();
55 this.queue = new ReferenceQueue<>();
56 }
57
58 @SuppressWarnings("unchecked")
59 <K, V> JexlCache<K, V> createCache(final int capacity) {
60 if (capacity > 0) {
61 final JexlCache<K, V> cache = (JexlCache<K, V>) factory.apply(capacity);
62 if (cache != null) {
63 synchronized (references) {
64 // The reference is created with the queue for automatic cleanup
65 references.add(new WeakReference<>(cache, queue));
66 // Always clean up the queue after modification to keep the set tidy
67 cleanUp();
68 }
69 }
70 return cache;
71 }
72 return null;
73 }
74
75 /**
76 * Clears all caches tracked by this MetaCache.
77 */
78 void clearCaches() {
79 synchronized (references) {
80 for (final Reference<JexlCache<?, ?>> ref : references) {
81 final JexlCache<?, ?> cache = ref.get();
82 if (cache != null) {
83 cache.clear();
84 }
85 }
86 cleanUp();
87 }
88 }
89
90 /**
91 * Cleans up all references whose referent (the cache) has been garbage collected.
92 * <p>This method must be invoked while holding the lock on {@code references}.</p>
93 *
94 * @return The remaining number of caches.
95 */
96 @SuppressWarnings("unchecked")
97 private int cleanUp() {
98 // The poll() method returns the next reference object in the queue, or null if none is available.
99 Reference<JexlCache<?, ?>> reference;
100 while ((reference = (Reference<JexlCache<?, ?>>) queue.poll()) != null) {
101 // Remove the reference from the set
102 references.remove(reference);
103 }
104 return references.size();
105 }
106
107 /**
108 * Gets the number of live caches currently tracked by this MetaCache.
109 *
110 * <p>Any cache that is no longer strongly reference will get removed from the
111 * tracked set.</p>
112 * @return The number of caches.
113 */
114 int size() {
115 synchronized (references) {
116 return cleanUp();
117 }
118 }
119 }