From 5c2c2042dd6c3d4f84d744b34dd8c7b3e5ded55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksy=20Wr=C3=B3blewski?= Date: Mon, 21 Nov 2022 21:30:31 +0100 Subject: [PATCH] [KARAF-6017] - added cache module --- assemblies/features/standard/pom.xml | 17 ++ .../standard/src/main/feature/feature.xml | 12 + bom/pom.xml | 27 +++ cache/api/pom.xml | 53 +++++ .../apache/karaf/cache/api/CacheService.java | 89 ++++++++ cache/commands/pom.xml | 69 ++++++ .../core/commands/CacheCommandSupport.java | 76 +++++++ .../karaf/cache/core/commands/Create.java | 50 +++++ .../apache/karaf/cache/core/commands/Get.java | 43 ++++ .../karaf/cache/core/commands/Invalidate.java | 41 ++++ .../karaf/cache/core/commands/ListCaches.java | 33 +++ .../apache/karaf/cache/core/commands/Put.java | 47 ++++ .../completers/CacheNameCompleter.java | 50 +++++ cache/core/pom.xml | 87 ++++++++ .../apache/karaf/cache/core/Activator.java | 73 ++++++ .../karaf/cache/core/EhcacheService.java | 88 ++++++++ .../karaf/cache/core/EhcacheServiceTest.java | 159 +++++++++++++ .../src/test/resources/test-cache-config.xml | 28 +++ cache/pom.xml | 88 ++++++++ examples/karaf-cache-example/README.md | 87 ++++++++ .../karaf-cache-example-client/pom.xml | 89 ++++++++ .../karaf/cache/example/client/Book.java | 44 ++++ .../example/client/CacheInitializer.java | 79 +++++++ .../src/main/resources/book-cache.xml | 31 +++ .../karaf-cache-example-feature/pom.xml | 75 +++++++ .../src/main/feature/feature.xml | 26 +++ examples/karaf-cache-example/pom.xml | 40 ++++ .../karaf-graphql-example-commands/pom.xml | 1 - .../karaf-maven-example-assembly/pom.xml | 2 +- examples/pom.xml | 1 + itests/test/pom.xml | 5 + .../test/filtered-resources/etc/feature.xml | 13 ++ .../org/apache/karaf/itests/CacheTest.java | 72 ++++++ .../itests/examples/CacheExampleTest.java | 44 ++++ .../test/resources/etc/test-cache-config.xml | 28 +++ manual/src/main/asciidoc/index.adoc | 2 + .../src/main/asciidoc/user-guide/cache.adoc | 210 ++++++++++++++++++ pom.xml | 6 +- 38 files changed, 1982 insertions(+), 3 deletions(-) create mode 100644 cache/api/pom.xml create mode 100644 cache/api/src/main/java/org/apache/karaf/cache/api/CacheService.java create mode 100644 cache/commands/pom.xml create mode 100644 cache/commands/src/main/java/org/apache/karaf/cache/core/commands/CacheCommandSupport.java create mode 100644 cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Create.java create mode 100644 cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Get.java create mode 100644 cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Invalidate.java create mode 100644 cache/commands/src/main/java/org/apache/karaf/cache/core/commands/ListCaches.java create mode 100644 cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Put.java create mode 100644 cache/commands/src/main/java/org/apache/karaf/cache/core/commands/completers/CacheNameCompleter.java create mode 100644 cache/core/pom.xml create mode 100644 cache/core/src/main/java/org/apache/karaf/cache/core/Activator.java create mode 100644 cache/core/src/main/java/org/apache/karaf/cache/core/EhcacheService.java create mode 100644 cache/core/src/test/java/org/apache/karaf/cache/core/EhcacheServiceTest.java create mode 100644 cache/core/src/test/resources/test-cache-config.xml create mode 100644 cache/pom.xml create mode 100644 examples/karaf-cache-example/README.md create mode 100644 examples/karaf-cache-example/karaf-cache-example-client/pom.xml create mode 100644 examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/Book.java create mode 100644 examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/CacheInitializer.java create mode 100644 examples/karaf-cache-example/karaf-cache-example-client/src/main/resources/book-cache.xml create mode 100644 examples/karaf-cache-example/karaf-cache-example-feature/pom.xml create mode 100644 examples/karaf-cache-example/karaf-cache-example-feature/src/main/feature/feature.xml create mode 100644 examples/karaf-cache-example/pom.xml create mode 100644 itests/test/src/test/java/org/apache/karaf/itests/CacheTest.java create mode 100644 itests/test/src/test/java/org/apache/karaf/itests/examples/CacheExampleTest.java create mode 100644 itests/test/src/test/resources/etc/test-cache-config.xml create mode 100644 manual/src/main/asciidoc/user-guide/cache.adoc diff --git a/assemblies/features/standard/pom.xml b/assemblies/features/standard/pom.xml index 2577560164d..67b00b30623 100644 --- a/assemblies/features/standard/pom.xml +++ b/assemblies/features/standard/pom.xml @@ -367,6 +367,23 @@ provided + + + org.apache.karaf.cache + org.apache.karaf.cache.api + provided + + + org.apache.karaf.cache + org.apache.karaf.cache.core + provided + + + org.apache.karaf.cache + org.apache.karaf.cache.commands + provided + + org.apache.karaf.services diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml index ecab7e3df65..8da3990c717 100644 --- a/assemblies/features/standard/src/main/feature/feature.xml +++ b/assemblies/features/standard/src/main/feature/feature.xml @@ -1671,6 +1671,18 @@ jul.layout.type = simple + + scr + mvn:javax.cache/cache-api/${spec.javax.cache-api.version} + mvn:org.ehcache/ehcache/${ehcache.version} + mvn:org.apache.karaf.cache/org.apache.karaf.cache.api/${project.version} + mvn:org.apache.karaf.cache/org.apache.karaf.cache.core/${project.version} + + shell + mvn:org.apache.karaf.cache/org.apache.karaf.cache.commands/${project.version} + + + pax-web-war mvn:org.apache.karaf/manual/${project.version} diff --git a/bom/pom.xml b/bom/pom.xml index 6480fc9c8d6..6747d612a38 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -435,6 +435,22 @@ ${project.version} + + org.apache.karaf.cache + org.apache.karaf.cache.api + ${project.version} + + + org.apache.karaf.cache + org.apache.karaf.cache.core + ${project.version} + + + org.apache.karaf.cache + org.apache.karaf.cache.commands + ${project.version} + + org.apache.karaf.services org.apache.karaf.services.staticcm @@ -1599,6 +1615,11 @@ istack-commons-runtime 3.0.10 + + org.ehcache + ehcache + ${ehcache.version} + @@ -1641,6 +1662,12 @@ jakarta.mail-api ${spec.mail.version} + + javax.cache + cache-api + ${spec.javax.cache-api.version} + + diff --git a/cache/api/pom.xml b/cache/api/pom.xml new file mode 100644 index 00000000000..91a89cc4162 --- /dev/null +++ b/cache/api/pom.xml @@ -0,0 +1,53 @@ + + + + + + cache + org.apache.karaf.cache + 4.4.3-SNAPSHOT + ../pom.xml + + 4.0.0 + + org.apache.karaf.cache.api + Apache Karaf :: Cache :: API + + bundle + + + + javax.cache + cache-api + + + + + + + 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..41dd90b359b --- /dev/null +++ b/cache/api/src/main/java/org/apache/karaf/cache/api/CacheService.java @@ -0,0 +1,89 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.cache.api; + +import javax.cache.Cache; +import javax.cache.configuration.Configuration; +import java.net.URL; +import java.util.List; + +/** + * A simple caching facade that lets use caching using JSR 107 APIs. + */ +public interface CacheService { + + /** + * Creates a new cache. + * @param name name of the cache to create + * @param configuration cache configuration, such as its key and value type. + * Specific configurations (such as max size and caching duration) + * will depend on the cache provider. + * @param type of cache keys, e.g. Long or String + * @param type of cache values, e.g. Long or String + */ + void createCache(String name, Configuration configuration); + + /** + * Creates a new cache from a configuration file (located in Karaf's etc directory). + * @param configFile config file + * @param classLoader class loader can be passed in case the cache complex custom keys and/or values + */ + void createCache(URL configFile, ClassLoader classLoader); + + /** + * Get a single cached value by providing cache's name and a key. + * @param name name of the cache to get the value from + * @param key key of the cached item + * @param cache key type + * @param cache value type + * @return + */ + V get(String name, K key); + + /** + * Store a new value in a cache. + * @param name name of the cache to put new value into + * @param key key under which the value will be cached + * @param value value to cache + * @param cache key type + * @param cache value type + */ + void put(String name, K key, V value); + + /** + * Get the cache object directly. + * @param name name of the cache + * @param cache key type + * @param cache value type + * @return + */ + Cache getCache(String name); + + /** + * Invalidates a cache. + * @param name name of the cache to invalidate + */ + void invalidateCache(String name); + + /** + * Lists all available caches by their names. + * @return list of cache names + */ + List listCaches(); +} diff --git a/cache/commands/pom.xml b/cache/commands/pom.xml new file mode 100644 index 00000000000..ff9d300349c --- /dev/null +++ b/cache/commands/pom.xml @@ -0,0 +1,69 @@ + + + + + + cache + org.apache.karaf.cache + 4.4.3-SNAPSHOT + ../pom.xml + + 4.0.0 + Apache Karaf :: Cache :: Commands + + org.apache.karaf.cache.commands + + bundle + + + + org.apache.karaf.shell + org.apache.karaf.shell.core + + + org.apache.karaf.cache + org.apache.karaf.cache.api + ${project.version} + + + org.ehcache + ehcache + + + + + + + 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/CacheCommandSupport.java b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/CacheCommandSupport.java new file mode 100644 index 00000000000..5803fde77a2 --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/CacheCommandSupport.java @@ -0,0 +1,76 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.lifecycle.Reference; + +import javax.cache.Cache; +import javax.cache.configuration.Configuration; + +public abstract class CacheCommandSupport implements Action +{ + + @Reference + private CacheService cacheService; + + @Override + public Object execute() throws Exception + { + if (cacheService == null) { + throw new IllegalStateException("CacheService not found"); + } + return doExecute(cacheService); + } + + protected abstract Object doExecute(CacheService cacheService) throws Exception; + + @SuppressWarnings("unchecked") + protected Object castKey(String cacheName, Object key) { + Cache cache = cacheService.getCache(cacheName); + if (cache == null) { + throw new IllegalArgumentException("Cache " + cacheName + " not found!"); + } + return cast(key, cacheService.getCache(cacheName).getConfiguration(Configuration.class).getKeyType()); + } + + @SuppressWarnings("unchecked") + protected Object castValue(String cacheName, Object value) { + return cast(value, cacheService.getCache(cacheName).getConfiguration(Configuration.class).getValueType()); + } + + private Object cast(Object argument, Class type) + { + if (type.equals(Short.class)) { + return Short.parseShort((String) argument); + } + if (type.equals(Integer.class)) { + return Integer.parseInt((String) argument); + } + if (type.equals(Long.class)) { + return Long.parseLong((String) argument); + } + if (type.equals(Boolean.class)) { + return Boolean.parseBoolean((String) argument); + } + + return argument; + } +} 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..892e64c9f2d --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Create.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.cache.core.commands; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Paths; + +@Service +@Command(scope = "cache", + name = "create", + description = "Create a new cache from XML config.") +public class Create extends CacheCommandSupport { + @Argument(index = 0, + required = true, + description = "Path to ehcache xml configuration file in Karaf's etc directory") + String configPath; + + @Override + protected Object doExecute(CacheService cacheService) { + try { + URL configUrl = Paths.get(System.getProperty("karaf.etc"), configPath).toUri().toURL(); + cacheService.createCache(configUrl, this.getClass().getClassLoader()); + } catch (MalformedURLException e) { + System.err.println(e); + } + + 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..af7b4b4cd5e --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Get.java @@ -0,0 +1,43 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.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.Service; + +@Service +@Command(scope = "cache", name = "get", description = "Get a single value from a given cache.") +public class Get extends CacheCommandSupport { + + @Argument(index = 0, required = true, description = "Name of the cache to access.") + @Completion(CacheNameCompleter.class) + String cacheName; + + @Argument(index = 1, required = true, description = "Key storing the value that we wish to check.") + Object key; + + @Override + public Object doExecute(CacheService cacheService) throws Exception { + return "" + cacheService.get(cacheName, castKey(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..976607d49e1 --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Invalidate.java @@ -0,0 +1,41 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.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.Service; + +@Service +@Command(scope = "cache", name = "invalidate", description = "Invalidates a cache identified by the provided name.") +public class Invalidate extends CacheCommandSupport { + + @Argument(index = 0, required = true) + @Completion(CacheNameCompleter.class) + String cacheName; + + @Override + public Object doExecute(CacheService cacheService) throws Exception { + cacheService.invalidateCache(cacheName); + return cacheName + " 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..e04741c253f --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/ListCaches.java @@ -0,0 +1,33 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.cache.core.commands; + +import org.apache.karaf.cache.api.CacheService; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Service +@Command(scope = "cache", name = "list", description = "Lists all available cache names.") +public class ListCaches extends CacheCommandSupport { + + @Override + public Object doExecute(CacheService cacheService) 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..8c2b30389a7 --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/Put.java @@ -0,0 +1,47 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.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.Service; + +@Service +@Command(scope = "cache", name = "put", description = "Stores a new value in a cache.") +public class Put extends CacheCommandSupport { + + @Argument(index = 0, required = true, description = "Name of the cache.") + @Completion(CacheNameCompleter.class) + String cacheName; + + @Argument(index = 1, required = true, description = "Key under which the value will be stored.") + Object key; + + @Argument(index = 2, required = true, description = "Value to cache.") + Object value; + + @Override + public Object doExecute(CacheService cacheService) throws Exception { + cacheService.put(cacheName, castKey(cacheName, key), castValue(cacheName, 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..6e3cd5ec306 --- /dev/null +++ b/cache/commands/src/main/java/org/apache/karaf/cache/core/commands/completers/CacheNameCompleter.java @@ -0,0 +1,50 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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..584b3c0a84f --- /dev/null +++ b/cache/core/pom.xml @@ -0,0 +1,87 @@ + + + + + + cache + org.apache.karaf.cache + 4.4.3-SNAPSHOT + ../pom.xml + + 4.0.0 + Apache Karaf :: Cache :: Core + + bundle + + org.apache.karaf.cache.core + + + + org.apache.karaf.cache + org.apache.karaf.cache.api + ${project.version} + + + javax.cache + cache-api + + + org.ehcache + ehcache + + + 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..c14294c5e62 --- /dev/null +++ b/cache/core/src/main/java/org/apache/karaf/cache/core/Activator.java @@ -0,0 +1,73 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.ProvideService; +import org.apache.karaf.util.tracker.annotation.Services; +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.util.Arrays; +import java.util.Optional; + +@Services(provides = @ProvideService(CacheService.class)) +public class Activator extends BaseActivator implements ManagedService { + + private EhcacheService ehcacheService; + + @Override + protected void doStart() { + logger.info("Cache bundle starting.."); + setUpEhcacheClassloading(); + CachingProvider provider = Caching.getCachingProvider(); + ehcacheService = new EhcacheService(provider.getCacheManager()); + register(CacheService.class, ehcacheService); + logger.info("Cache bundle started!"); + } + + /** + * 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..201ad328336 --- /dev/null +++ b/cache/core/src/main/java/org/apache/karaf/cache/core/EhcacheService.java @@ -0,0 +1,88 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.cache.core; + +import org.apache.karaf.cache.api.CacheService; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.jsr107.Eh107Configuration; +import org.ehcache.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.configuration.Configuration; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Implementation of CacheService based on Ehcache. + */ +public class EhcacheService implements CacheService { + + private final CacheManager cacheManager; + private final Logger logger = LoggerFactory.getLogger(EhcacheService.class); + + public EhcacheService(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @Override + public void createCache(String name, Configuration configuration) { + cacheManager.createCache(name, configuration); + } + + @Override + public void createCache(URL configFile, ClassLoader classLoader) { + XmlConfiguration xmlConfiguration = new XmlConfiguration(configFile, classLoader); + Map> configurationMap = xmlConfiguration.getCacheConfigurations(); + configurationMap.forEach( + (name, config) -> createCache(name, Eh107Configuration.fromEhcacheCacheConfiguration(config))); + logger.info("Loaded ehcache xml configuration from " + configFile); + } + + @Override + @SuppressWarnings("unchecked") + public V get(String name, K key) { + return (V) getCache(name).get(key); + } + + @Override + public void put(String name, K key, V value) { + cacheManager.getCache(name).put(key, value); + } + + @Override + public Cache getCache(String name) { + return cacheManager.getCache(name); + } + + @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/core/src/test/java/org/apache/karaf/cache/core/EhcacheServiceTest.java b/cache/core/src/test/java/org/apache/karaf/cache/core/EhcacheServiceTest.java new file mode 100644 index 00000000000..b7acec966c7 --- /dev/null +++ b/cache/core/src/test/java/org/apache/karaf/cache/core/EhcacheServiceTest.java @@ -0,0 +1,159 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.cache.core; + +import org.easymock.EasyMockRunner; +import org.easymock.Mock; +import org.ehcache.config.ResourcePools; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.jsr107.Eh107Configuration; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.configuration.Configuration; +import java.net.URL; +import java.util.Arrays; +import java.util.List; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.assertEquals; + +@RunWith(EasyMockRunner.class) +public class EhcacheServiceTest { + + @Mock + private CacheManager cacheManager; + + @Test + public void testCreateCacheWithConfigurationObject() { + String cacheName = "Test"; + Cache cache = mock(Cache.class); + + ResourcePools pools = ResourcePoolsBuilder.heap(100).build(); + Configuration configuration = Eh107Configuration.fromEhcacheCacheConfiguration( + CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Long.class, pools).build()); + expect(cacheManager.createCache(cacheName, configuration)).andReturn(cache); + + replay(cacheManager, cache); + + EhcacheService ehcacheService = new EhcacheService(cacheManager); + + ehcacheService.createCache(cacheName, configuration); + + verify(cacheManager, cache); + } + + @Test + public void testCreateCacheWithConfigurationUrl() { + String cacheName = "TestCache"; + URL configFile = getClass().getResource("/test-cache-config.xml"); + + Cache cache = mock(Cache.class); + expect(cacheManager.createCache(eq(cacheName), anyObject(Configuration.class))) + .andReturn(cache); + + replay(cacheManager, cache); + + EhcacheService ehcacheService = new EhcacheService(cacheManager); + + ehcacheService.createCache(configFile, this.getClass().getClassLoader()); + + verify(cacheManager); + } + + @Test + public void testListCaches() { + List cacheNames = Arrays.asList("C1", "C2"); + expect(cacheManager.getCacheNames()).andReturn(cacheNames); + + replay(cacheManager); + + EhcacheService ehcacheService = new EhcacheService(cacheManager); + List caches = ehcacheService.listCaches(); + assertEquals(cacheNames, caches); + } + + @Test + public void testInvalidateCache() { + String cacheName = "Test"; + cacheManager.destroyCache(cacheName); + expectLastCall(); + + replay(cacheManager); + + EhcacheService ehcacheService = new EhcacheService(cacheManager); + ehcacheService.invalidateCache(cacheName); + verify(cacheManager); + } + + @Test + public void testGetCache() { + String cacheName = "Test"; + Cache objCache = mock(Cache.class); + expect(cacheManager.getCache(cacheName)).andReturn(objCache); + + replay(cacheManager); + + EhcacheService ehcacheService = new EhcacheService(cacheManager); + + ehcacheService.getCache(cacheName); + + verify(cacheManager); + } + + @Test + public void testGet() { + String cacheName = "Test"; + String key = "k"; + String value = "v"; + + Cache cache = mock(Cache.class); + expect(cacheManager.getCache(cacheName)).andReturn(cache); + expect(cache.get(key)).andReturn(value); + + replay(cacheManager, cache); + + EhcacheService ehcacheService = new EhcacheService(cacheManager); + + Object result = ehcacheService.get(cacheName, key); + assertEquals(value, result); + verify(cacheManager); + } + + @Test + public void testPut() { + String cacheName = "Test"; + String key = "k"; + String value = "v"; + + Cache cache = mock(Cache.class); + expect(cacheManager.getCache(cacheName)).andReturn(cache); + cache.put(key, value); + expectLastCall(); + + replay(cacheManager, cache); + + EhcacheService ehcacheService = new EhcacheService(cacheManager); + + ehcacheService.put(cacheName, key, value); + verify(cacheManager); + } +} diff --git a/cache/core/src/test/resources/test-cache-config.xml b/cache/core/src/test/resources/test-cache-config.xml new file mode 100644 index 00000000000..cce48556e85 --- /dev/null +++ b/cache/core/src/test/resources/test-cache-config.xml @@ -0,0 +1,28 @@ + + + + + java.lang.String + java.lang.String + + + 2 + + 200 + + diff --git a/cache/pom.xml b/cache/pom.xml new file mode 100644 index 00000000000..5db6ceffa36 --- /dev/null +++ b/cache/pom.xml @@ -0,0 +1,88 @@ + + + + + + 4.0.0 + + api + core + commands + + + + org.apache.karaf + karaf + 4.4.3-SNAPSHOT + ../pom.xml + + + org.apache.karaf.cache + cache + pom + Apache Karaf :: Cache :: Core Parent POM + Cache Facade + + + + + org.apache.karaf + karaf-bom + ${project.version} + pom + import + + + javax.cache + cache-api + ${spec.javax.cache-api.version} + + + org.ehcache + ehcache + ${ehcache.version} + + + org.slf4j + slf4j-api + provided + + + org.apache.felix + org.apache.felix.utils + provided + + + org.apache.karaf + org.apache.karaf.util + provided + ${project.version} + + + + + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/examples/karaf-cache-example/README.md b/examples/karaf-cache-example/README.md new file mode 100644 index 00000000000..c8925172748 --- /dev/null +++ b/examples/karaf-cache-example/README.md @@ -0,0 +1,87 @@ + +# Apache Karaf Cache example + +## Abstract + +This example shows how to use a simple cache facade provided by Karaf. + +## Build +The build uses Apache Maven. Simply use: + +``` +mvn clean install +``` + +## Deployment +On a running Karaf instance, add a feature repository and then the feature: +``` +karaf@root()> feature:repo-add mvn:org.apache.karaf.examples/karaf-cache-example-feature/LATEST/xml +karaf@root()> feature:install karaf-cache-example +``` + +## Usage +Check the source code of karaf-cache-example-client to see how to programmatically use Karaf's *CacheService* by creating a cache with Ehcache configuration builders or by providing XML configuration file. When using XML config files, a *ClassLoader* needs to be passed so Ehcache can load keys and values other than standard Java types like String and Long, otherwise it won't work in OSGi environment. It can also be passed in the *Configuration* object. + +*CacheService* can be obtained via standard OSGi practices such as *ServiceTracker*, Blueprint or Declarative Services. + + +You can try out the *cache* commands: +``` +karaf@root()> cache:list +ExampleCache +BookCache + +karaf@root()> cache:get BookCache 1 +Book{title='Apache Karaf Cookbook', numOfPages=789} +karaf@root()> cache:invalidate BookCache +BookCache invalidated +karaf@root()> cache:get BookCache 1 +Error executing command: Cache BookCache not found! + +``` +To create a new cache via a command, put an Ehcache XML configuration file in your Karaf's etc directory, for example: +``` + + + java.lang.Long + java.lang.String + + + 2 + + + 200 + + + +``` +In this case the file name is `config.xml`. +``` +karaf@root()> cache:create config.xml +karaf@root()> cache:put TestCache 1 Hello +karaf@root()> cache:put TestCache 2 World +karaf@root()> cache:get TestCache 1 +Hello +karaf@root()> cache:get TestCache 2 +World +``` + + + diff --git a/examples/karaf-cache-example/karaf-cache-example-client/pom.xml b/examples/karaf-cache-example/karaf-cache-example-client/pom.xml new file mode 100644 index 00000000000..463afa24ced --- /dev/null +++ b/examples/karaf-cache-example/karaf-cache-example-client/pom.xml @@ -0,0 +1,89 @@ + + + + + karaf-cache-example + org.apache.karaf.examples + 4.4.3-SNAPSHOT + ../pom.xml + + + Apache Karaf :: Examples :: Cache :: Client + karaf-cache-example-client + + 4.0.0 + + bundle + + + + org.apache.karaf.cache + org.apache.karaf.cache.api + ${project.version} + + + org.ehcache + ehcache + ${ehcache.version} + + + org.glassfish.jaxb + jaxb-runtime + + + + + org.slf4j + slf4j-api + provided + ${slf4j.version} + + + org.osgi + osgi.core + ${osgi.version} + + + org.osgi + org.osgi.service.component.annotations + ${org.osgi.service.component.annotations.version} + provided + + + + + + + ${project.basedir}/src/main/resources + true + + **/* + + + + + + + org.apache.felix + maven-bundle-plugin + + + + \ No newline at end of file diff --git a/examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/Book.java b/examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/Book.java new file mode 100644 index 00000000000..6b385ee2af5 --- /dev/null +++ b/examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/Book.java @@ -0,0 +1,44 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.cache.example.client; + +/** + * This is to showcase caching of a more complex object, rather than something like String or Long. + */ +public class Book { + private final String title; + private final int numOfPages; + + public Book(String title, int numOfPages) { + this.title = title; + this.numOfPages = numOfPages; + } + + public String getTitle() { + return title; + } + + public int getNumOfPages() { + return numOfPages; + } + + @Override + public String toString() { + return "Book{" + "title='" + title + '\'' + ", numOfPages=" + numOfPages + '}'; + } +} diff --git a/examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/CacheInitializer.java b/examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/CacheInitializer.java new file mode 100644 index 00000000000..9b00d0b2efb --- /dev/null +++ b/examples/karaf-cache-example/karaf-cache-example-client/src/main/java/org/apache/karaf/cache/example/client/CacheInitializer.java @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.cache.example.client; + +import org.apache.karaf.cache.api.CacheService; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.jsr107.Eh107Configuration; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URL; +import java.time.Duration; + +@Component(immediate = true) +public class CacheInitializer { + + private final Logger logger = LoggerFactory.getLogger(CacheInitializer.class); + + @Reference + private CacheService cacheService; + + @Activate + public void activate() { + initCaches(); + } + + private void initCaches() { + initConfigBuilderCache(); + initXmlConfigCache(); + } + + private void initConfigBuilderCache() { + String cacheName = "ExampleCache"; + logger.info("Creating " + cacheName + " and populating it with some values.."); + + CacheConfiguration cacheConfiguration = CacheConfigurationBuilder + .newCacheConfigurationBuilder(Long.class, Book.class, ResourcePoolsBuilder.heap(50)) + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(1))) + .build(); + cacheService.createCache(cacheName, Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration)); + cacheService.put(cacheName, 1L, new Book("Effective Java", 123)); + cacheService.put(cacheName, 2L, new Book("OSGi in Action", 456)); + logger.info("Cache value under key 1: " + cacheService.get(cacheName, 1L)); + logger.info("Cache value under key 2: " + cacheService.get(cacheName, 2L)); + } + + private void initXmlConfigCache() { + String cacheName = "BookCache"; + logger.info("Creating " + cacheName + " and populating it with some values.."); + + URL url = FrameworkUtil.getBundle(this.getClass()).getEntry("/book-cache.xml"); + cacheService.createCache(url, this.getClass().getClassLoader()); + cacheService.put(cacheName, 1L, new Book("Apache Karaf Cookbook", 789)); + + logger.info("Cache value under key 1: " + cacheService.get(cacheName, 1L)); + } +} diff --git a/examples/karaf-cache-example/karaf-cache-example-client/src/main/resources/book-cache.xml b/examples/karaf-cache-example/karaf-cache-example-client/src/main/resources/book-cache.xml new file mode 100644 index 00000000000..2b74839936b --- /dev/null +++ b/examples/karaf-cache-example/karaf-cache-example-client/src/main/resources/book-cache.xml @@ -0,0 +1,31 @@ + + + + + java.lang.Long + org.apache.karaf.cache.example.client.Book + + + 2 + + + 200 + + diff --git a/examples/karaf-cache-example/karaf-cache-example-feature/pom.xml b/examples/karaf-cache-example/karaf-cache-example-feature/pom.xml new file mode 100644 index 00000000000..2d9fe70acbe --- /dev/null +++ b/examples/karaf-cache-example/karaf-cache-example-feature/pom.xml @@ -0,0 +1,75 @@ + + + + + + karaf-cache-example + org.apache.karaf.examples + 4.4.3-SNAPSHOT + + 4.0.0 + Apache Karaf :: Examples :: Cache :: Features + + karaf-cache-example-feature + + + + + src/main/feature + true + ${project.build.directory}/feature + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + resources + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + package + + attach-artifact + + + + + target/feature/feature.xml + xml + + + + + + + + + \ No newline at end of file diff --git a/examples/karaf-cache-example/karaf-cache-example-feature/src/main/feature/feature.xml b/examples/karaf-cache-example/karaf-cache-example-feature/src/main/feature/feature.xml new file mode 100644 index 00000000000..b8cea12a7ae --- /dev/null +++ b/examples/karaf-cache-example/karaf-cache-example-feature/src/main/feature/feature.xml @@ -0,0 +1,26 @@ + + + + + + cache + mvn:org.apache.karaf.examples/karaf-cache-example-client/${project.version} + + + diff --git a/examples/karaf-cache-example/pom.xml b/examples/karaf-cache-example/pom.xml new file mode 100644 index 00000000000..3d3b6ba9644 --- /dev/null +++ b/examples/karaf-cache-example/pom.xml @@ -0,0 +1,40 @@ + + + + + + 4.0.0 + + + org.apache.karaf.examples + apache-karaf-examples + 4.4.3-SNAPSHOT + ../pom.xml + + + karaf-cache-example + Apache Karaf :: Examples :: Cache + pom + + + karaf-cache-example-feature + karaf-cache-example-client + + + \ No newline at end of file diff --git a/examples/karaf-graphql-example/karaf-graphql-example-commands/pom.xml b/examples/karaf-graphql-example/karaf-graphql-example-commands/pom.xml index c81654ddc4c..2b2dc9defc1 100644 --- a/examples/karaf-graphql-example/karaf-graphql-example-commands/pom.xml +++ b/examples/karaf-graphql-example/karaf-graphql-example-commands/pom.xml @@ -1,6 +1,5 @@ - + + + java.lang.String + java.lang.String + + + 2 + + 200 + + diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc index eb809e76222..966359340b4 100644 --- a/manual/src/main/asciidoc/index.adoc +++ b/manual/src/main/asciidoc/index.adoc @@ -92,6 +92,8 @@ include::user-guide/scheduler.adoc[] include::user-guide/tuning.adoc[] +include::user-guide/cache.adoc[] + == Developer Guide include::developer-guide/developer-commands.adoc[] diff --git a/manual/src/main/asciidoc/user-guide/cache.adoc b/manual/src/main/asciidoc/user-guide/cache.adoc new file mode 100644 index 00000000000..a4a92a0f4a0 --- /dev/null +++ b/manual/src/main/asciidoc/user-guide/cache.adoc @@ -0,0 +1,210 @@ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +=== Caching + +Apache Karaf provides an optional caching module which offers a simple caching service based on JSR 107 APIs. +The implementation uses Ehcache but other providers can also be used. + +==== Installation + +To enable the Apache Karaf Cache, you just have to install the `cache` feature: + +---- +karaf@root()> feature:install cache +---- + +The `cache` feature automatically installs the `cache` command group, too: + +---- +cache:list +cache:get +cache:put +cache:create +cache:invalidate +---- + +==== Configuration +The caches can be created from Ehcache XML configurations. +These can be put for example in your bundle's resources or in Karaf's etc directory. + + +==== Using the CacheService +This example uses Declarative Services to get an instance of CacheService from your bundle, and then showcases the available methods. +``` +import org.apache.karaf.cache.api.CacheService; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.jsr107.Eh107Configuration; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.util.List; + +@Component(immediate = true) +public class CacheInitializer { + + private final Logger logger = LoggerFactory.getLogger(CacheInitializer.class); + + @Reference + private CacheService cacheService; + + @Activate + public void activate() { + String cacheName = "ExampleCache"; + CacheConfiguration cacheConfiguration = CacheConfigurationBuilder + .newCacheConfigurationBuilder(Long.class, Book.class, ResourcePoolsBuilder.heap(50)) + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(1))) + .build(); + List caches = cacheService.listCaches(); // [ExampleCache] + cacheService.createCache(cacheName, Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration)); + cacheService.put(cacheName, 1L, new Book("Effective Java", 123)); + cacheService.invalidateCache(cacheName); + caches = cacheService.listCaches(); // [] + } +} + +``` + +==== Create cache via Karaf shell +This assumes that there is an Ehcache 3 compliant cache-config.xml file in Karaf's etc directory with, like the following: +---- + + + java.lang.String + java.lang.String + + + 2 + + 200 + + +---- + +---- +karaf@root()> cache:create --help +DESCRIPTION + cache:create + + Create a new cache from XML config. + +SYNTAX + cache:create configPath + +ARGUMENTS + configPath + Path to ehcache xml configuration file in Karaf's etc directory + (required) + +karaf@root()> cache:create cache-config.xml +---- + +==== List all cache names +---- +karaf@root()> cache:list --help +DESCRIPTION + cache:list + + Lists all available cache names. + +SYNTAX + cache:list + +karaf@root()> cache:list +ExampleCache +BookCache +---- + +==== Put values into the cache +While Ehcache normally allows using POJOs as keys and values, this will not work via Karaf shell, only simple types (String, Long, Integer, Short, Boolean) can be used. This is because the cache command bundle will probably not + have this POJO in its classloader. +Recommendation: if using POJOs as cache keys or values, test them programmatically and not via Shell. + +---- +karaf@root()> cache:put --help +DESCRIPTION + cache:put + + Stores a new value in a cache. + +SYNTAX + cache:put cacheName key value + +ARGUMENTS + cacheName + Name of the cache. + (required) + key + Key under which the value will be stored. + (required) + value + Value to cache. + (required) + +karaf@root()> cache:put TestCache hello world +karaf@root()> cache:get TestCache hello +world +---- + +==== Get values in the cache +If the cache uses POJOs as values, its `toString()` representation will be displayed. +---- +karaf@root()> cache:get --help +DESCRIPTION + cache:get + + Get a single value from a given cache. + +SYNTAX + cache:get cacheName key + +ARGUMENTS + cacheName + Name of the cache to access. + (required) + key + Key storing the value that we wish to check. + (required) + +karaf@root()> cache:get BookCache 1 +Book{title='Apache Karaf Cookbook', numOfPages=789} +---- + +==== Invalidate the cache + +---- +karaf@root()> cache:invalidate --help +DESCRIPTION + cache:invalidate + + Invalidates a cache identified by the provided name. + +SYNTAX + cache:invalidate cacheName + +ARGUMENTS + cacheName + + (required) + +karaf@root()> cache:invalidate BookCache +BookCache invalidated +---- diff --git a/pom.xml b/pom.xml index a88719678ef..9f79e798c2e 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,7 @@ tooling manual specs + cache assemblies examples archetypes @@ -321,6 +322,8 @@ 3.4.2 1.7.32 + 3.10.0 + 1.2.2 1.3.5 2.3.2_3 @@ -328,7 +331,8 @@ 2.3 1.1.1 1.6.5 - 2.3.2_2 + 2.3.2_2 + 1.1.0 1.2.1 3.1.4.RELEASE