{"id":18336550,"url":"https://github.com/kumuluz/kumuluzee-jcache","last_synced_at":"2025-04-09T19:55:57.657Z","repository":{"id":57726021,"uuid":"217494941","full_name":"kumuluz/kumuluzee-jcache","owner":"kumuluz","description":"KumuluzEE JCache for implementing caching in KumuluzEE microservices using JCache annotations. ","archived":false,"fork":false,"pushed_at":"2022-12-01T11:20:38.000Z","size":63,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-09T19:55:52.887Z","etag":null,"topics":["caching","jakartaee","java","javaee","jcache","jcache-annotations","kumuluzee","microservices"],"latest_commit_sha":null,"homepage":"https://ee.kumuluz.com","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kumuluz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-25T09:03:48.000Z","updated_at":"2022-12-14T23:07:15.000Z","dependencies_parsed_at":"2023-01-22T14:00:55.616Z","dependency_job_id":null,"html_url":"https://github.com/kumuluz/kumuluzee-jcache","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-jcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-jcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-jcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-jcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kumuluz","download_url":"https://codeload.github.com/kumuluz/kumuluzee-jcache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103911,"owners_count":21048245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caching","jakartaee","java","javaee","jcache","jcache-annotations","kumuluzee","microservices"],"created_at":"2024-11-05T20:08:15.169Z","updated_at":"2025-04-09T19:55:57.637Z","avatar_url":"https://github.com/kumuluz.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KumuluzEE JCache\n\n[![KumuluzEE CI](https://github.com/kumuluz/kumuluzee-jcache/actions/workflows/kumuluzee-ci.yml/badge.svg)](https://github.com/kumuluz/kumuluzee-jcache/actions/workflows/kumuluzee-ci.yml)\n\nKumuluzEE JCache allows usage of JCache annotations and JCache programmatic API in your KumuluzEE applications.\nCurrent implementation used is [Caffeine](https://github.com/ben-manes/caffeine).\n\nFor sample project, take a look at `kumuluzee-jcache` module in [kumuluzee-samples](https://github.com/kumuluz/kumuluzee-samples) repository.\n\nThis readme is not a comprehensive guide to JCache but is meant to outline the basic tasks you can accomplish with JCache annotations and APIs.\n\nAdditional resources:\n- [tomitribe/microprofile-jcache](https://github.com/tomitribe/microprofile-jcache/tree/master/jcache-cdi)\n- [JCache JavaDoc](https://www.javadoc.io/doc/javax.cache/cache-api/1.1.1)\n\n## Maven dependency\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.kumuluz.ee.jcache\u003c/groupId\u003e\n    \u003cartifactId\u003ekumuluzee-jcache-caffeine\u003c/artifactId\u003e\n    \u003cversion\u003e${kumuluzee-jcache-caffeine.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Configuration\n\nYou can specify the configuration using `kumuluzee-config` YAML file and/or ENV variables.\nUse prefix `kumuluzee.jcache.caffeine`, followed by reference Caffeine configuration structure.\n\nBelow is a YAML version of Caffeine [reference configuration](https://github.com/ben-manes/caffeine/blob/c06c5cca5c80e578495b206d03c8f76fb74f7b22/jcache/src/main/resources/reference.conf):\n```yaml\nkumuluzee:\n  name: kumuluzee-jcache-sample-project\n  version: 1.0.0\n  env:\n    name: dev\n  jcache:\n    caffeine:\n      caches:\n        # A named cache is configured by nesting a new definition under the caches namespace.\n        # The per-cache configuration is overlaid on top of the default configuration.\n        default:\n          # The required type of the keys\n          key-type: java.lang.Object\n          # The required type of the values\n          value-type: java.lang.Object\n          # The strategy for copying the cache entry for value-based storage\n          store-by-value:\n            # If enabled, the entry is copied when crossing the API boundary\n            enabled: false\n            strategy: \"com.github.benmanes.caffeine.jcache.copy.JavaSerializationCopier\"\n          # The executor class to use when performing maintenance and asynchronous operations. Defaults to\n          # using ForkJoinPool.commonPool() if not set.\n          executor: null\n          # The list of configuration paths to the listeners that consume this cache's events\n          listeners: []\n          read-through:\n            # If enabled, the entry is loaded automatically on a cache miss\n            enabled: false\n            # The CacheLoader class for loading entries\n            loader: null\n          write-through:\n            # If enabled, the entry is written to the resource before the cache is updated\n            enabled: false\n            # The CacheWriter class for writing entries\n            writer: null\n          # The JMX monitoring configuration\n          monitoring :\n            # If cache statistics should be recorded and externalized\n            statistics: false\n            # If the configuration should be externalized\n            management: false\n          # The eviction policy for automatically removing entries from the cache\n          policy:\n            # The expiration threshold before lazily evicting an entry. This single threshold is reset on\n            # every operation where a duration is specified. As expected by the specification, if an entry\n            # expires but is not accessed and no resource constraints force eviction, then the expired\n            # entry remains in place.\n            lazy-expiration:\n              # The duration before a newly created entry is considered expired. If set to 0 then the\n              # entry is considered to be already expired and will not be added to the cache. May be\n              # a time duration or \"eternal\" to indicate no expiration.\n              creation: \"eternal\"\n              # The duration before a updated entry is considered expired. If set to 0 then the entry is\n              # considered immediately expired. May be a time duration, null to indicate no change, or\n              # \"eternal\" to indicate no expiration.\n              update: \"eternal\"\n              # The duration before a read of an entry is considered expired. If set to 0 then the entry\n              # is considered immediately expired. May be a time duration, null to indicate no change, or\n              # \"eternal\" to indicate no expiration.\n              access: \"eternal\"\n            # The expiration thresholds before eagerly evicting an entry. These settings correspond to the\n            # expiration supported natively by Caffeine where expired entries are collected during\n            # maintenance operations.\n            eager-expiration:\n              # Specifies that each entry should be automatically removed from the cache once a fixed\n              # duration has elapsed after the entry's creation, or the most recent replacement of its\n              # value. This setting cannot be combined with the variable configuration.\n              # Accepted time units are: d, h, m, s...\n              after-write: null\n              # Specifies that each entry should be automatically removed from the cache once a fixed\n              # duration has elapsed after the entry's creation, the most recent replacement of its value,\n              # or its last read. Access time is reset by all cache read and write operation. This setting\n              # cannot be combined with the variable configuration.\n              after-access: null\n              # The expiry class to use when calculating the expiration time of cache entries. This\n              # setting cannot be combined with after-write or after-access configurations.\n              variable: null\n            # The threshold before an entry is eligible to be automatically refreshed when the first stale\n            # request for an entry occurs. This setting is honored only when combined with the\n            # read-through configuration.\n            refresh:\n              # Specifies that active entries are eligible for automatic refresh once a fixed duration has\n              # elapsed after the entry's creation or the most recent replacement of its value.\n              after-write: null\n            # The maximum bounding of the cache based upon its logical size\n            maximum:\n              # The maximum number of entries that can be held by the cache. This setting cannot be\n              # combined with the weight configuration.\n              size: null\n              # The maximum total weight of entries the cache may contain (requires a weigher). This\n              # setting cannot be combined with the size configuration.\n              weight: null\n              # The weigher class to use when calculating the weight of cache entries\n              weigher: null\n\n        # A catalog of cache listeners; optionally defined in any namespace as referenced by path\n        listeners:\n          # An example definition of a listener\n          example:\n            # The CacheEntryListener class\n            class: null\n            # The CacheEntryEventFilter class that should be applied prior to notifying the listener\n            filter: null\n            # If the thread that created the event should block until the listener has completed\n            synchronous: false\n            # If the old value should be provided\n            old-value-required: false        \n```\n\nA basic example\n```yaml\nkumuluzee:\n  name: kumuluzee-jcache-sample-project\n  version: 1.0.0\n  env:\n    name: dev\n  jcache:\n    caffeine:\n      caches:\n        default:\n          policy:\n            eager-expiration:\n              after-write: \"10s\"\n            maximum:\n              size: 10000\n```\n\nYou can have multiple named caches (in the above example, cache name is `default`) and\nyou reference that name either in annotations or programmatic API.\n\n## JCache annotations and interceptors\n\nJCache annotations are an easy and clean way to introduce cache into your application. Note that only one interceptor may be used on a method at a time.\n\n### @CacheDefaults\n\nSets defaults at class level.\n\n```java\n@CacheDefaults(cacheName = \"default\")\npublic class Library {\n}\n```\n\n### @CacheKey, @CacheValue\n\n`@CacheKey` annotates a method parameter to specify it as a cache key. If no parameter is annotated, cache key is formed from all method parameters (excluding parameters annotated with `@CacheValue`). `@CacheValue` marks a parameter to be stored as a value. Default key implementation is `DefaultGeneratedCacheKey` which uses `Arrays.deepHashCode` and `Arrays.deepEquals` behind the scenes.\n\n### @CachePut (interceptor)\n\nPuts a cache value into the cache.\n\n```java\n@CachePut\npublic void updateBook(@CacheKey String id, @CacheValue Book book) {\n}\n```\n\n### @CacheResult (interceptor)\n\nReturns cached value if it exists, otherwise stores the method result in cache.\n\n```java\n@CacheResult\npublic Book getBook(@CacheKey String id) {\n}\n```\n\n### @CacheRemove (interceptor)\n\nRemoves an entry from cache after method completion.\n\n```java\n@CacheRemove\npublic Book removeBook(String id) {\n}\n```\n\n### @CacheRemoveAll (interceptor)\n\nRemoves all entries from cache.\n```java\n@CacheRemoveAll\npublic Book removeAllBooks() {\n}\n```\n\n## JCache programmatic API\n\n`Cache` object behaves similar to java `Map` but there are [differences](https://static.javadoc.io/javax.cache/cache-api/1.1.1/javax/cache/Cache.html)!\n\nBasic usage:\n\n```java\nCachingProvider cachingProvider = Caching.getCachingProvider();\nCacheManager cacheManager = cachingProvider.getCacheManager();\nMutableConfiguration\u003cString, String\u003e config = new MutableConfiguration\u003c\u003e();\nCache\u003cString, String\u003e cache = cacheManager.createCache(\"default\", config);\ncache.put(\"key1\", \"value1\");\ncacheManager.close();\n```\n\n### EntryProcessor\n\nEntryProcessor allows modifying Cache entries using atomic operations. Think: atomic counters, rate limiting..\n\n### Event Listeners\n\nYou can listen to the followig events:\n\n- CREATED\n- UPDATED\n- REMOVED\n- EXPIRED\n\n### CacheLoader\n\nUsed either to bulk fill cache from external source or for read-through operations (invokes loader on cache miss to fetch the data from another source such as database).\n\n## Cache CDI producer example\n\n### Static configuration\n\n```java\npublic class CacheProducer {\n\n    @Inject\n    private CacheManager cacheManager;\n\n    @Produces\n    @ApplicationScoped\n    public Cache\u003cString, String\u003e createCache() {\n        return cacheManager.getCache(\"default\");\n    }\n}\n```\n\n### Dynamic configuration\n\n```java\npublic class CacheProducer {\n\n    @Inject\n    private CacheManager cacheManager;\n\n    @Produces\n    @ApplicationScoped\n    public Cache\u003cString, String\u003e createCache() {\n        final Configuration\u003cString, String\u003e configuration = new MutableConfiguration\u003cString, String\u003e().setTypes(String.class, String.class);\n        return cacheManager.createCache(\"default\", configuration);\n    }\n}\n```\n\n## Disable caching\n\nYou can disable caching by setting `policy.maximumSize` to zero.\n\n## Interop between annotations and programmatic API\n\nIt is a general recommendation **not** to mix annotations and programmatic API for the same named cache. Use annotations for simple method caching and programmatic API when you need full control. Also be aware that it is not possible to access annotated cache values from programmatic API, at least not in an easy and clean way. For example:\n\n```java\n@CachePut(cacheName = \"default\")\npublic void put(@CacheKey String key, @CacheValue String data) {\n}\n\npublic String get(String key) {\n    if (cache.containsKey(key)) { //This is never true\n        return cache.get(key);\n    }\n}\n```\n\nLooking from a high level, this should work but it does not due to different key being used in each case so the keys never match. Annotation key by default is an instance of `DefaultGeneratedCacheKey`.\n\nTo avoid any such pitfalls, use separate named caches for annotations and programmatic API.\n\n## Known issues\n\n### CacheKeyGenerator must be a CDI bean\n\nDue to an implementation detail in upstream `cache-ri-impl`, a custom CacheKeyGenerator must be a thread safe CDI bean and not just a plain class.\n\n```\n@CachePut(cacheKeyGenerator=MyKeyGenerator.class)\n```\n```\n@ApplicationScoped\npublic class MyKeyGenerator implements CacheKeyGenerator {\n\n    @Override\n    public GeneratedCacheKey generateCacheKey(CacheKeyInvocationContext\u003c? extends Annotation\u003e cacheKeyInvocationContext) {\n        return null;\n    }\n}\n```\n\n## Contribute\n\nSee the [contributing docs](https://github.com/kumuluz/kumuluzee-jcache/blob/master/CONTRIBUTING.md)\n\nWhen submitting an issue, please follow the \n[guidelines](https://github.com/kumuluz/kumuluzee-jcache/blob/master/CONTRIBUTING.md#bugs).\n\nWhen submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test \nalongside the fix.\n\nWhen submitting a new feature, add tests that cover the feature.\n\n## License\n\nThis project is under MIT license.\n\nSubset of files are under Apache license taken from `tomitribe/microprofile-jcache` project. See license headers in the respective source files.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkumuluz%2Fkumuluzee-jcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkumuluz%2Fkumuluzee-jcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkumuluz%2Fkumuluzee-jcache/lists"}