1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.jcs3.jcache;
20
21 import static org.apache.commons.jcs3.jcache.Asserts.assertNotNull;
22
23 import java.io.ByteArrayInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.net.URI;
27 import java.net.URL;
28 import java.nio.charset.StandardCharsets;
29 import java.util.Enumeration;
30 import java.util.Map;
31 import java.util.Properties;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.concurrent.ConcurrentMap;
34
35 import javax.cache.Cache;
36 import javax.cache.CacheManager;
37 import javax.cache.configuration.Configuration;
38 import javax.cache.spi.CachingProvider;
39
40 import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes;
41 import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
42 import org.apache.commons.jcs3.engine.control.CompositeCache;
43 import org.apache.commons.jcs3.engine.control.CompositeCacheConfigurator;
44 import org.apache.commons.jcs3.engine.control.CompositeCacheManager;
45 import org.apache.commons.jcs3.jcache.lang.Subsitutor;
46 import org.apache.commons.jcs3.jcache.proxy.ClassLoaderAwareCache;
47
48 public class JCSCachingManager implements CacheManager
49 {
50 private static final Subsitutor SUBSTITUTOR = Subsitutor.Helper.INSTANCE;
51 private static final String DEFAULT_CONFIG =
52 "jcs.default=DC\n" +
53 "jcs.default.cacheattributes=org.apache.commons.jcs3.engine.CompositeCacheAttributes\n" +
54 "jcs.default.cacheattributes.MaxObjects=200001\n" +
55 "jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs3.engine.memory.lru.LRUMemoryCache\n" +
56 "jcs.default.cacheattributes.UseMemoryShrinker=true\n" +
57 "jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600\n" +
58 "jcs.default.cacheattributes.ShrinkerIntervalSeconds=60\n" +
59 "jcs.default.elementattributes=org.apache.commons.jcs3.engine.ElementAttributes\n" +
60 "jcs.default.elementattributes.IsEternal=false\n" +
61 "jcs.default.elementattributes.MaxLife=700\n" +
62 "jcs.default.elementattributes.IdleTime=1800\n" +
63 "jcs.default.elementattributes.IsSpool=true\n" +
64 "jcs.default.elementattributes.IsRemote=true\n" +
65 "jcs.default.elementattributes.IsLateral=true\n";
66
67 private static class InternalManager extends CompositeCacheManager
68 {
69 protected static InternalManager create()
70 {
71 return new InternalManager();
72 }
73
74 @Override
75 protected CompositeCacheConfigurator newConfigurator()
76 {
77 return new CompositeCacheConfigurator()
78 {
79 @Override
80 protected <K, V> CompositeCache<K, V> newCache(
81 final ICompositeCacheAttributes cca, final IElementAttributes ea)
82 {
83 return new ExpiryAwareCache<>( cca, ea );
84 }
85 };
86 }
87
88 @Override
89 protected void initialize() {
90 super.initialize();
91 }
92 }
93
94 private final CachingProvider provider;
95 private final URI uri;
96 private final ClassLoader loader;
97 private final Properties properties;
98 private final ConcurrentMap<String, Cache<?, ?>> caches = new ConcurrentHashMap<>();
99 private final Properties configProperties;
100 private volatile boolean closed;
101 private final InternalManager delegate = InternalManager.create();
102
103 public JCSCachingManager(final CachingProvider provider, final URI uri, final ClassLoader loader, final Properties properties)
104 {
105 this.provider = provider;
106 this.uri = uri;
107 this.loader = loader;
108 this.properties = readConfig(uri, loader, properties);
109 this.configProperties = properties;
110
111 delegate.setJmxName(CompositeCacheManager.JMX_OBJECT_NAME
112 + ",provider=" + provider.hashCode()
113 + ",uri=" + uri.toString().replaceAll(",|:|=|\n", ".")
114 + ",classloader=" + loader.hashCode()
115 + ",properties=" + this.properties.hashCode());
116 delegate.initialize();
117 delegate.configure(this.properties);
118 }
119
120 private static Properties readConfig(final URI uri, final ClassLoader loader, final Properties properties) {
121 final Properties props = new Properties();
122 try {
123 if (JCSCachingProvider.DEFAULT_URI.toString().equals(uri.toString()) || uri.toURL().getProtocol().equals("jcs"))
124 {
125
126 final Enumeration<URL> resources = loader.getResources(uri.getPath());
127 if (!resources.hasMoreElements())
128 {
129 props.load(new ByteArrayInputStream(DEFAULT_CONFIG.getBytes(StandardCharsets.UTF_8)));
130 }
131 else
132 {
133 do
134 {
135 addProperties(resources.nextElement(), props);
136 }
137 while (resources.hasMoreElements());
138 }
139 }
140 else
141 {
142 props.load(uri.toURL().openStream());
143 }
144 } catch (final IOException e) {
145 throw new IllegalStateException(e);
146 }
147
148 if (properties != null)
149 {
150 props.putAll(properties);
151 }
152
153 for (final Map.Entry<Object, Object> entry : props.entrySet()) {
154 if (entry.getValue() == null)
155 {
156 continue;
157 }
158 final String substitute = SUBSTITUTOR.substitute(entry.getValue().toString());
159 if (!substitute.equals(entry.getValue()))
160 {
161 entry.setValue(substitute);
162 }
163 }
164 return props;
165 }
166
167 private static void addProperties(final URL url, final Properties aggregator)
168 {
169 try (InputStream inStream = url.openStream()) {
170 aggregator.load(inStream);
171 } catch (final IOException e) {
172 throw new IllegalArgumentException(e);
173 }
174 }
175
176 private void assertNotClosed()
177 {
178 if (isClosed())
179 {
180 throw new IllegalStateException("cache manager closed");
181 }
182 }
183
184 @Override
185
186 public <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(final String cacheName, final C configuration)
187 throws IllegalArgumentException
188 {
189 assertNotClosed();
190 assertNotNull(cacheName, "cacheName");
191 assertNotNull(configuration, "configuration");
192 final Class<K> keyType = configuration.getKeyType();
193 final Class<V> valueType = configuration.getValueType();
194 if (caches.containsKey(cacheName)) {
195 throw new javax.cache.CacheException("cache " + cacheName + " already exists");
196 }
197 @SuppressWarnings("unchecked")
198 final Cache<K, V> cache = ClassLoaderAwareCache.wrap(loader,
199 new JCSCache<>(
200 loader, this, cacheName,
201 new JCSConfiguration<K, V>(configuration, keyType, valueType),
202 properties,
203 ExpiryAwareCache.class.cast(delegate.getCache(cacheName))));
204 caches.putIfAbsent(cacheName, cache);
205 return getCache(cacheName, keyType, valueType);
206 }
207
208 @Override
209 public void destroyCache(final String cacheName)
210 {
211 assertNotClosed();
212 assertNotNull(cacheName, "cacheName");
213 final Cache<?, ?> cache = caches.remove(cacheName);
214 if (cache != null && !cache.isClosed())
215 {
216 cache.clear();
217 cache.close();
218 }
219 }
220
221 @Override
222 public void enableManagement(final String cacheName, final boolean enabled)
223 {
224 assertNotClosed();
225 assertNotNull(cacheName, "cacheName");
226 final JCSCache<?, ?> cache = getJCSCache(cacheName);
227 if (cache != null)
228 {
229 if (enabled)
230 {
231 cache.enableManagement();
232 }
233 else
234 {
235 cache.disableManagement();
236 }
237 }
238 }
239
240 private JCSCache<?, ?> getJCSCache(final String cacheName)
241 {
242 final Cache<?, ?> cache = caches.get(cacheName);
243 return JCSCache.class.cast(ClassLoaderAwareCache.getDelegate(cache));
244 }
245
246 @Override
247 public void enableStatistics(final String cacheName, final boolean enabled)
248 {
249 assertNotClosed();
250 assertNotNull(cacheName, "cacheName");
251 final JCSCache<?, ?> cache = getJCSCache(cacheName);
252 if (cache != null)
253 {
254 if (enabled)
255 {
256 cache.enableStatistics();
257 }
258 else
259 {
260 cache.disableStatistics();
261 }
262 }
263 }
264
265 @Override
266 public synchronized void close()
267 {
268 if (isClosed())
269 {
270 return;
271 }
272
273 assertNotClosed();
274 for (final Cache<?, ?> c : caches.values())
275 {
276 c.close();
277 }
278 caches.clear();
279 closed = true;
280 if (JCSCachingProvider.class.isInstance(provider))
281 {
282 JCSCachingProvider.class.cast(provider).remove(this);
283 }
284 delegate.shutDown();
285 }
286
287 @Override
288 public <T> T unwrap(final Class<T> clazz)
289 {
290 if (clazz.isInstance(this))
291 {
292 return clazz.cast(this);
293 }
294 throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
295 }
296
297 @Override
298 public boolean isClosed()
299 {
300 return closed;
301 }
302
303 @Override
304 public <K, V> Cache<K, V> getCache(final String cacheName)
305 {
306 assertNotClosed();
307 assertNotNull(cacheName, "cacheName");
308 return (Cache<K, V>) doGetCache(cacheName, Object.class, Object.class);
309 }
310
311 @Override
312 public Iterable<String> getCacheNames()
313 {
314 return new ImmutableIterable<>(caches.keySet());
315 }
316
317 @Override
318 public <K, V> Cache<K, V> getCache(final String cacheName, final Class<K> keyType, final Class<V> valueType)
319 {
320 assertNotClosed();
321 assertNotNull(cacheName, "cacheName");
322 assertNotNull(keyType, "keyType");
323 assertNotNull(valueType, "valueType");
324 try
325 {
326 return doGetCache(cacheName, keyType, valueType);
327 }
328 catch (final IllegalArgumentException iae)
329 {
330 throw new ClassCastException(iae.getMessage());
331 }
332 }
333
334 private <K, V> Cache<K, V> doGetCache(final String cacheName, final Class<K> keyType, final Class<V> valueType)
335 {
336 @SuppressWarnings("unchecked")
337 final Cache<K, V> cache = (Cache<K, V>) caches.get(cacheName);
338 if (cache == null)
339 {
340 return null;
341 }
342
343 @SuppressWarnings("unchecked")
344 final Configuration<K, V> config = cache.getConfiguration(Configuration.class);
345 if (keyType != null && !config.getKeyType().isAssignableFrom(keyType) ||
346 valueType != null && !config.getValueType().isAssignableFrom(valueType))
347 {
348 throw new IllegalArgumentException("this cache is <" + config.getKeyType().getName() + ", " + config.getValueType().getName()
349 + "> " + " and not <" + keyType.getName() + ", " + valueType.getName() + ">");
350 }
351 return cache;
352 }
353
354 @Override
355 public CachingProvider getCachingProvider()
356 {
357 return provider;
358 }
359
360 @Override
361 public URI getURI()
362 {
363 return uri;
364 }
365
366 @Override
367 public ClassLoader getClassLoader()
368 {
369 return loader;
370 }
371
372 @Override
373 public Properties getProperties()
374 {
375 return configProperties;
376 }
377
378 public void release(final String name) {
379 caches.remove(name);
380 }
381 }