JCSCachingManager.java

  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.jcs3.jcache;

  20. import static org.apache.commons.jcs3.jcache.Asserts.assertNotNull;

  21. import java.io.ByteArrayInputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.net.URI;
  25. import java.net.URL;
  26. import java.nio.charset.StandardCharsets;
  27. import java.util.Enumeration;
  28. import java.util.Map;
  29. import java.util.Properties;
  30. import java.util.concurrent.ConcurrentHashMap;
  31. import java.util.concurrent.ConcurrentMap;

  32. import javax.cache.Cache;
  33. import javax.cache.CacheManager;
  34. import javax.cache.configuration.Configuration;
  35. import javax.cache.spi.CachingProvider;

  36. import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes;
  37. import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
  38. import org.apache.commons.jcs3.engine.control.CompositeCache;
  39. import org.apache.commons.jcs3.engine.control.CompositeCacheConfigurator;
  40. import org.apache.commons.jcs3.engine.control.CompositeCacheManager;
  41. import org.apache.commons.jcs3.jcache.lang.Subsitutor;
  42. import org.apache.commons.jcs3.jcache.proxy.ClassLoaderAwareCache;

  43. public class JCSCachingManager implements CacheManager
  44. {
  45.     private static final Subsitutor SUBSTITUTOR = Subsitutor.Helper.INSTANCE;
  46.     private static final String DEFAULT_CONFIG =
  47.         "jcs.default=DC\n" +
  48.         "jcs.default.cacheattributes=org.apache.commons.jcs3.engine.CompositeCacheAttributes\n" +
  49.         "jcs.default.cacheattributes.MaxObjects=200001\n" +
  50.         "jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs3.engine.memory.lru.LRUMemoryCache\n" +
  51.         "jcs.default.cacheattributes.UseMemoryShrinker=true\n" +
  52.         "jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600\n" +
  53.         "jcs.default.cacheattributes.ShrinkerIntervalSeconds=60\n" +
  54.         "jcs.default.elementattributes=org.apache.commons.jcs3.engine.ElementAttributes\n" +
  55.         "jcs.default.elementattributes.IsEternal=false\n" +
  56.         "jcs.default.elementattributes.MaxLife=700\n" +
  57.         "jcs.default.elementattributes.IdleTime=1800\n" +
  58.         "jcs.default.elementattributes.IsSpool=true\n" +
  59.         "jcs.default.elementattributes.IsRemote=true\n" +
  60.         "jcs.default.elementattributes.IsLateral=true\n";

  61.     private static class InternalManager extends CompositeCacheManager
  62.     {
  63.         protected static InternalManager create()
  64.         {
  65.             return new InternalManager();
  66.         }

  67.         @Override
  68.         protected CompositeCacheConfigurator newConfigurator()
  69.         {
  70.             return new CompositeCacheConfigurator()
  71.             {
  72.                 @Override
  73.                 protected <K, V> CompositeCache<K, V> newCache(
  74.                         final ICompositeCacheAttributes cca, final IElementAttributes ea)
  75.                 {
  76.                     return new ExpiryAwareCache<>( cca, ea );
  77.                 }
  78.             };
  79.         }

  80.         @Override // needed to call it from JCSCachingManager
  81.         protected void initialize() {
  82.             super.initialize();
  83.         }
  84.     }

  85.     private final CachingProvider provider;
  86.     private final URI uri;
  87.     private final ClassLoader loader;
  88.     private final Properties properties;
  89.     private final ConcurrentMap<String, Cache<?, ?>> caches = new ConcurrentHashMap<>();
  90.     private final Properties configProperties;
  91.     private volatile boolean closed;
  92.     private final InternalManager delegate = InternalManager.create();

  93.     public JCSCachingManager(final CachingProvider provider, final URI uri, final ClassLoader loader, final Properties properties)
  94.     {
  95.         this.provider = provider;
  96.         this.uri = uri;
  97.         this.loader = loader;
  98.         this.properties = readConfig(uri, loader, properties);
  99.         this.configProperties = properties;

  100.         delegate.setJmxName(CompositeCacheManager.JMX_OBJECT_NAME
  101.                 + ",provider=" + provider.hashCode()
  102.                 + ",uri=" + uri.toString().replaceAll(",|:|=|\n", ".")
  103.                 + ",classloader=" + loader.hashCode()
  104.                 + ",properties=" + this.properties.hashCode());
  105.         delegate.initialize();
  106.         delegate.configure(this.properties);
  107.     }

  108.     private static Properties readConfig(final URI uri, final ClassLoader loader, final Properties properties) {
  109.         final Properties props = new Properties();
  110.         try {
  111.             if (JCSCachingProvider.DEFAULT_URI.toString().equals(uri.toString()) || uri.toURL().getProtocol().equals("jcs"))
  112.             {

  113.                 final Enumeration<URL> resources = loader.getResources(uri.getPath());
  114.                 if (!resources.hasMoreElements()) // default
  115.                 {
  116.                     props.load(new ByteArrayInputStream(DEFAULT_CONFIG.getBytes(StandardCharsets.UTF_8)));
  117.                 }
  118.                 else
  119.                 {
  120.                     do
  121.                     {
  122.                         addProperties(resources.nextElement(), props);
  123.                     }
  124.                     while (resources.hasMoreElements());
  125.                 }
  126.             }
  127.             else
  128.             {
  129.                 props.load(uri.toURL().openStream());
  130.             }
  131.         } catch (final IOException e) {
  132.             throw new IllegalStateException(e);
  133.         }

  134.         if (properties != null)
  135.         {
  136.             props.putAll(properties);
  137.         }

  138.         for (final Map.Entry<Object, Object> entry : props.entrySet()) {
  139.             if (entry.getValue() == null)
  140.             {
  141.                 continue;
  142.             }
  143.             final String substitute = SUBSTITUTOR.substitute(entry.getValue().toString());
  144.             if (!substitute.equals(entry.getValue()))
  145.             {
  146.                 entry.setValue(substitute);
  147.             }
  148.         }
  149.         return props;
  150.     }

  151.     private static void addProperties(final URL url, final Properties aggregator)
  152.     {
  153.         try (InputStream inStream = url.openStream()) {
  154.             aggregator.load(inStream);
  155.         } catch (final IOException e) {
  156.             throw new IllegalArgumentException(e);
  157.         }
  158.     }

  159.     private void assertNotClosed()
  160.     {
  161.         if (isClosed())
  162.         {
  163.             throw new IllegalStateException("cache manager closed");
  164.         }
  165.     }

  166.     @Override
  167.     // TODO: use configuration + handle not serializable key/values
  168.     public <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(final String cacheName, final C configuration)
  169.             throws IllegalArgumentException
  170.     {
  171.         assertNotClosed();
  172.         assertNotNull(cacheName, "cacheName");
  173.         assertNotNull(configuration, "configuration");
  174.         final Class<K> keyType = configuration.getKeyType();
  175.         final Class<V> valueType = configuration.getValueType();
  176.         if (caches.containsKey(cacheName)) {
  177.             throw new javax.cache.CacheException("cache " + cacheName + " already exists");
  178.         }
  179.         @SuppressWarnings("unchecked")
  180.         final Cache<K, V> cache = ClassLoaderAwareCache.wrap(loader,
  181.                 new JCSCache<>(
  182.                         loader, this, cacheName,
  183.                         new JCSConfiguration<K, V>(configuration, keyType, valueType),
  184.                         properties,
  185.                         ExpiryAwareCache.class.cast(delegate.getCache(cacheName))));
  186.         caches.putIfAbsent(cacheName, cache);
  187.         return getCache(cacheName, keyType, valueType);
  188.     }

  189.     @Override
  190.     public void destroyCache(final String cacheName)
  191.     {
  192.         assertNotClosed();
  193.         assertNotNull(cacheName, "cacheName");
  194.         final Cache<?, ?> cache = caches.remove(cacheName);
  195.         if (cache != null && !cache.isClosed())
  196.         {
  197.             cache.clear();
  198.             cache.close();
  199.         }
  200.     }

  201.     @Override
  202.     public void enableManagement(final String cacheName, final boolean enabled)
  203.     {
  204.         assertNotClosed();
  205.         assertNotNull(cacheName, "cacheName");
  206.         final JCSCache<?, ?> cache = getJCSCache(cacheName);
  207.         if (cache != null)
  208.         {
  209.             if (enabled)
  210.             {
  211.                 cache.enableManagement();
  212.             }
  213.             else
  214.             {
  215.                 cache.disableManagement();
  216.             }
  217.         }
  218.     }

  219.     private JCSCache<?, ?> getJCSCache(final String cacheName)
  220.     {
  221.         final Cache<?, ?> cache = caches.get(cacheName);
  222.         return JCSCache.class.cast(ClassLoaderAwareCache.getDelegate(cache));
  223.     }

  224.     @Override
  225.     public void enableStatistics(final String cacheName, final boolean enabled)
  226.     {
  227.         assertNotClosed();
  228.         assertNotNull(cacheName, "cacheName");
  229.         final JCSCache<?, ?> cache = getJCSCache(cacheName);
  230.         if (cache != null)
  231.         {
  232.             if (enabled)
  233.             {
  234.                 cache.enableStatistics();
  235.             }
  236.             else
  237.             {
  238.                 cache.disableStatistics();
  239.             }
  240.         }
  241.     }

  242.     @Override
  243.     public synchronized void close()
  244.     {
  245.         if (isClosed())
  246.         {
  247.             return;
  248.         }

  249.         assertNotClosed();
  250.         for (final Cache<?, ?> c : caches.values())
  251.         {
  252.             c.close();
  253.         }
  254.         caches.clear();
  255.         closed = true;
  256.         if (JCSCachingProvider.class.isInstance(provider))
  257.         {
  258.             JCSCachingProvider.class.cast(provider).remove(this);
  259.         }
  260.         delegate.shutDown();
  261.     }

  262.     @Override
  263.     public <T> T unwrap(final Class<T> clazz)
  264.     {
  265.         if (clazz.isInstance(this))
  266.         {
  267.             return clazz.cast(this);
  268.         }
  269.         throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
  270.     }

  271.     @Override
  272.     public boolean isClosed()
  273.     {
  274.         return closed;
  275.     }

  276.     @Override
  277.     public <K, V> Cache<K, V> getCache(final String cacheName)
  278.     {
  279.         assertNotClosed();
  280.         assertNotNull(cacheName, "cacheName");
  281.         return (Cache<K, V>) doGetCache(cacheName, Object.class, Object.class);
  282.     }

  283.     @Override
  284.     public Iterable<String> getCacheNames()
  285.     {
  286.         return new ImmutableIterable<>(caches.keySet());
  287.     }

  288.     @Override
  289.     public <K, V> Cache<K, V> getCache(final String cacheName, final Class<K> keyType, final Class<V> valueType)
  290.     {
  291.         assertNotClosed();
  292.         assertNotNull(cacheName, "cacheName");
  293.         assertNotNull(keyType, "keyType");
  294.         assertNotNull(valueType, "valueType");
  295.         try
  296.         {
  297.             return doGetCache(cacheName, keyType, valueType);
  298.         }
  299.         catch (final IllegalArgumentException iae)
  300.         {
  301.             throw new ClassCastException(iae.getMessage());
  302.         }
  303.     }

  304.     private <K, V> Cache<K, V> doGetCache(final String cacheName, final Class<K> keyType, final Class<V> valueType)
  305.     {
  306.         @SuppressWarnings("unchecked") // common map for all caches
  307.         final Cache<K, V> cache = (Cache<K, V>) caches.get(cacheName);
  308.         if (cache == null)
  309.         {
  310.             return null;
  311.         }

  312.         @SuppressWarnings("unchecked") // don't know how to solve this
  313.         final Configuration<K, V> config = cache.getConfiguration(Configuration.class);
  314.         if (keyType != null && !config.getKeyType().isAssignableFrom(keyType) ||
  315.             valueType != null && !config.getValueType().isAssignableFrom(valueType))
  316.         {
  317.             throw new IllegalArgumentException("this cache is <" + config.getKeyType().getName() + ", " + config.getValueType().getName()
  318.                     + "> " + " and not <" + keyType.getName() + ", " + valueType.getName() + ">");
  319.         }
  320.         return cache;
  321.     }

  322.     @Override
  323.     public CachingProvider getCachingProvider()
  324.     {
  325.         return provider;
  326.     }

  327.     @Override
  328.     public URI getURI()
  329.     {
  330.         return uri;
  331.     }

  332.     @Override
  333.     public ClassLoader getClassLoader()
  334.     {
  335.         return loader;
  336.     }

  337.     @Override
  338.     public Properties getProperties()
  339.     {
  340.         return configProperties;
  341.     }

  342.     public void release(final String name) {
  343.         caches.remove(name);
  344.     }
  345. }