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