View Javadoc
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 }