diff --git a/cache/api/pom.xml b/cache/api/pom.xml new file mode 100644 index 00000000000..075768e6a16 --- /dev/null +++ b/cache/api/pom.xml @@ -0,0 +1,55 @@ + + + + + + cache + org.apache.karaf.cache + 4.4.2-SNAPSHOT + ../pom.xml + + 4.0.0 + + api + + bundle + + + + javax.cache + cache-api + 1.1.0 + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + + + \ No newline at end of file diff --git a/cache/api/src/main/java/org/apache/karaf/cache/api/CacheService.java b/cache/api/src/main/java/org/apache/karaf/cache/api/CacheService.java new file mode 100644 index 00000000000..4a65d7c7489 --- /dev/null +++ b/cache/api/src/main/java/org/apache/karaf/cache/api/CacheService.java @@ -0,0 +1,17 @@ +package org.apache.karaf.cache.api; + +import javax.cache.configuration.Configuration; +import java.util.List; + +public interface CacheService { + + void createCache(String name, Configuration configuration); + + Object get(String name, Object key); + + void put(String name, Object key, Object value); + + void invalidateCache(String name); + + List listCaches(); +} diff --git a/cache/commands/pom.xml b/cache/commands/pom.xml new file mode 100644 index 00000000000..2951e24f4ef --- /dev/null +++ b/cache/commands/pom.xml @@ -0,0 +1,65 @@ + + + + + + cache + org.apache.karaf.cache + 4.4.2-SNAPSHOT + ../pom.xml + + 4.0.0 + + commands + + bundle + + + + org.apache.karaf.shell + org.apache.karaf.shell.core + + + org.apache.karaf.cache + api + ${project.version} + + + + + + + org.apache.karaf.tooling + karaf-services-maven-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + * + + + + + + \ No newline at end of file diff --git a/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Create.java b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Create.java new file mode 100644 index 00000000000..4146d4ba22f --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Create.java @@ -0,0 +1,39 @@ +package org.apache.karaf.cache.core.commands; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +import javax.cache.configuration.Configuration; +import javax.cache.configuration.MutableConfiguration; +import javax.cache.expiry.CreatedExpiryPolicy; +import javax.cache.expiry.Duration; + +@Service +@Command(scope = "cache", name = "create", description = "") +public class Create implements Action { + + @Argument(index = 0, required = true) + String cacheName; + @Argument(index = 1, required = true) + String keyType; + @Argument(index = 2, required = true) + String valueType; + @Reference + private CacheService cacheService; + + @Override + public Object execute() throws Exception { + Class keyClass = Class.forName(keyType); + Class valueClass = Class.forName(valueType); + Configuration configuration = new MutableConfiguration<>() + .setTypes(keyClass, valueClass) + .setStoreByValue(false) + .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE)); + cacheService.createCache(cacheName, configuration); + return null; + } +} diff --git a/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Get.java b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Get.java new file mode 100644 index 00000000000..6288e2a951b --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Get.java @@ -0,0 +1,30 @@ +package org.apache.karaf.cache.core.commands; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.cache.core.commands.completers.CacheNameCompleter; +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Service +@Command(scope = "cache", name = "get", description = "") +public class Get implements Action { + + @Reference + CacheService cacheService; + + @Argument(index = 0, required = true) + @Completion(CacheNameCompleter.class) + String cacheName; + + @Argument(index = 1, required = true) + Object key; + + @Override + public Object execute() throws Exception { + return cacheService.get(cacheName, key); + } +} diff --git a/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Invalidate.java b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Invalidate.java new file mode 100644 index 00000000000..acadf21f73d --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Invalidate.java @@ -0,0 +1,28 @@ +package org.apache.karaf.cache.core.commands; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.cache.core.commands.completers.CacheNameCompleter; +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Service +@Command(scope = "cache", name = "invalidate", description = "") +public class Invalidate implements Action { + + @Reference + CacheService cacheService; + + @Argument(index = 0, required = true) + @Completion(CacheNameCompleter.class) + String cacheName; + + @Override + public Object execute() throws Exception { + cacheService.invalidateCache(cacheName); + return cacheService + " invalidated"; + } +} diff --git a/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/ListCaches.java b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/ListCaches.java new file mode 100644 index 00000000000..126117a9e49 --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/ListCaches.java @@ -0,0 +1,20 @@ +package org.apache.karaf.cache.core.commands; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Service +@Command(scope = "cache", name = "list", description = "") +public class ListCaches implements Action { + + @Reference + CacheService cacheService; + + @Override + public Object execute() throws Exception { + return cacheService.listCaches(); + } +} diff --git a/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Put.java b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Put.java new file mode 100644 index 00000000000..a510ab5c32c --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Put.java @@ -0,0 +1,34 @@ +package org.apache.karaf.cache.core.commands; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.cache.core.commands.completers.CacheNameCompleter; +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Service +@Command(scope = "cache", name = "put", description = "") +public class Put implements Action { + + @Reference + private CacheService cacheService; + + @Argument(index = 0, required = true) + @Completion(CacheNameCompleter.class) + String cacheName; + + @Argument(index = 1, required = true) + Object key; + + @Argument(index = 2, required = true) + Object value; + + @Override + public Object execute() throws Exception { + cacheService.put(cacheName, key, value); + return null; + } +} diff --git a/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/completers/CacheNameCompleter.java b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/completers/CacheNameCompleter.java new file mode 100644 index 00000000000..6c49da5745b --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/completers/CacheNameCompleter.java @@ -0,0 +1,32 @@ +package org.apache.karaf.cache.core.commands.completers; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; +import org.apache.karaf.shell.api.console.CommandLine; +import org.apache.karaf.shell.api.console.Completer; +import org.apache.karaf.shell.api.console.Session; +import org.apache.karaf.shell.support.completers.StringsCompleter; + +import java.util.List; + +@Service +public class CacheNameCompleter implements Completer { + + @Reference + CacheService cacheService; + + @Override + public int complete(Session session, CommandLine commandLine, List candidates) { + StringsCompleter delegate = new StringsCompleter(); + try { + for (String cache : cacheService.listCaches()) { + delegate.getStrings().add(cache); + } + } catch (Exception e) { + // nothing to do + } + + return delegate.complete(session, commandLine, candidates); + } +} diff --git a/cache/core/pom.xml b/cache/core/pom.xml new file mode 100644 index 00000000000..fb7d53331c1 --- /dev/null +++ b/cache/core/pom.xml @@ -0,0 +1,71 @@ + + + + cache + org.apache.karaf.cache + 4.4.2-SNAPSHOT + ../pom.xml + + 4.0.0 + + bundle + + core + + + + org.apache.karaf.cache + api + ${project.version} + + + org.ehcache + ehcache + 3.10.0 + + + javax.cache + cache-api + 1.1.0 + + + org.osgi + osgi.core + provided + + + org.apache.karaf + org.apache.karaf.util + + + org.osgi + org.osgi.service.cm + provided + + + + + + + org.apache.karaf.tooling + karaf-services-maven-plugin + + + org.apache.felix + + + + org.apache.karaf.util.tracker, + org.apache.karaf.util, + org.apache.karaf.util.tracker.annotation + + + + maven-bundle-plugin + true + + + + \ No newline at end of file diff --git a/cache/core/src/main/java/org/apache/karaf/cache/core/Activator.java b/cache/core/src/main/java/org/apache/karaf/cache/core/Activator.java new file mode 100644 index 00000000000..715bb8b9819 --- /dev/null +++ b/cache/core/src/main/java/org/apache/karaf/cache/core/Activator.java @@ -0,0 +1,87 @@ +package org.apache.karaf.cache.core; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.util.tracker.BaseActivator; +import org.apache.karaf.util.tracker.annotation.Managed; +import org.apache.karaf.util.tracker.annotation.ProvideService; +import org.apache.karaf.util.tracker.annotation.Services; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProviderFactory; +import org.ehcache.jsr107.Eh107Configuration; +import org.ehcache.xml.XmlConfiguration; +import org.osgi.framework.Bundle; +import org.osgi.framework.wiring.BundleWiring; +import org.osgi.service.cm.ManagedService; + +import javax.cache.Caching; +import javax.cache.spi.CachingProvider; +import java.io.File; +import java.net.MalformedURLException; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Dictionary; +import java.util.Map; +import java.util.Optional; + +@Services(provides = @ProvideService(CacheService.class)) +@Managed("org.apache.karaf.cache") +public class Activator extends BaseActivator implements ManagedService { + + private static final String XML_CONFIG_PATH = "xml.config.path"; + private EhcacheService ehcacheService; + + @Override + protected void doStart() throws Exception { + logger.info("Cache bundle starting.."); + setUpEhcacheClassloading(); + CachingProvider provider = Caching.getCachingProvider(); + ehcacheService = new EhcacheService(provider.getCacheManager()); + register(CacheService.class, ehcacheService); + registerConfiguredCaches(); + logger.info("Cache bundle started!"); + } + + private void registerConfiguredCaches() throws MalformedURLException { + Dictionary configuration = getConfiguration(); + + if (configuration == null) { + return; + } + + if (configuration.get(XML_CONFIG_PATH) != null) { + String xmlConfigFile = Paths.get(System.getProperty("karaf.etc"), + configuration.get(XML_CONFIG_PATH).toString()).toString(); + XmlConfiguration xmlConfiguration = new XmlConfiguration(new File(xmlConfigFile).toURI().toURL()); + Map> configurationMap = xmlConfiguration.getCacheConfigurations(); + configurationMap.forEach((name, config) -> ehcacheService.createCache( + name, Eh107Configuration.fromEhcacheCacheConfiguration(config))); + logger.info("Loaded ehcache xml configuration from " + xmlConfigFile); + } + } + + /** + * Tells JSR 107 where the implementation by its classloader to the ehcache bundle, + * otherwise Caching.getCachingProvider() throws an exception saying that it couldn't find any providers + * + */ + private void setUpEhcacheClassloading() { + logger.debug("Replacing JSR 107 classloader with the ehcache bundle"); + Optional ehcacheBundleOpt = Arrays.asList(bundleContext.getBundles()).stream() + .filter(bundle -> bundle.getSymbolicName().equals("org.ehcache")) + .findFirst(); + if (ehcacheBundleOpt.isPresent()) { + Bundle ehcacheBundle = ehcacheBundleOpt.get(); + BundleWiring bundleWiring = ehcacheBundle.adapt(BundleWiring.class); + ClassLoader classLoader = bundleWiring.getClassLoader(); + Caching.setDefaultClassLoader(classLoader); + } else { + throw new IllegalStateException("No ehcache bundle found!"); + } + } + + @Override + protected void doStop() { + super.doStop(); + ehcacheService = null; + } +} diff --git a/cache/core/src/main/java/org/apache/karaf/cache/core/EhcacheService.java b/cache/core/src/main/java/org/apache/karaf/cache/core/EhcacheService.java new file mode 100644 index 00000000000..b993a6992b5 --- /dev/null +++ b/cache/core/src/main/java/org/apache/karaf/cache/core/EhcacheService.java @@ -0,0 +1,44 @@ +package org.apache.karaf.cache.core; + +import org.apache.karaf.cache.api.CacheService; + +import javax.cache.CacheManager; +import javax.cache.configuration.Configuration; +import java.util.ArrayList; +import java.util.List; + +public class EhcacheService implements CacheService { + + private final CacheManager cacheManager; + + public EhcacheService(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @Override + public void createCache(String name, Configuration configuration) { + cacheManager.createCache(name, configuration); + } + + @Override + public Object get(String name, Object key) { + return cacheManager.getCache(name).get(key); + } + + @Override + public void put(String name, Object key, Object value) { + cacheManager.getCache(name).put(key, value); + } + + @Override + public void invalidateCache(String name) { + cacheManager.destroyCache(name); + } + + @Override + public List listCaches() { + List cacheNames = new ArrayList<>(); + cacheManager.getCacheNames().iterator().forEachRemaining(cacheNames::add); + return cacheNames; + } +} \ No newline at end of file diff --git a/cache/pom.xml b/cache/pom.xml new file mode 100644 index 00000000000..1e778424e41 --- /dev/null +++ b/cache/pom.xml @@ -0,0 +1,87 @@ + + + + + + 4.0.0 + + api + core + commands + + + + org.apache.karaf + karaf + 4.4.2-SNAPSHOT + ../pom.xml + + + org.apache.karaf.cache + cache + pom + Apache Karaf :: Cache :: Core Parent POM + Cache Facade + + + ${basedir}/../../etc/appended-resources + + + + + + org.apache.karaf + karaf-bom + ${project.version} + pom + import + + + + + + + org.slf4j + slf4j-api + provided + + + + org.apache.felix + org.apache.felix.utils + provided + + + + org.apache.karaf + org.apache.karaf.util + provided + + + + + + + org.apache.felix + maven-bundle-plugin + + + + + diff --git a/pom.xml b/pom.xml index f4a896281e8..0127057c404 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,7 @@ examples archetypes itests + cache