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