Permalink
Browse files

Replaced javax.cache API with custom api

The javax.cache api was not final and the available implementations were
limited to specific versions of (mainly) EhCache. This made using the api
virtually impossible.
By replacing it with a small custom API, it is easier to hook in specific
cache implementations, such as EhCache.

Issue #AXON-229 Fixed
  • Loading branch information...
abuijze committed Mar 28, 2014
1 parent 76026fe commit 596f783bcec4b64534973ddd1d20d45d487cc434
Showing with 697 additions and 723 deletions.
  1. +4 −15 core/pom.xml
  2. +55 −0 core/src/main/java/org/axonframework/cache/AbstractCacheAdapter.java
  3. +165 −0 core/src/main/java/org/axonframework/cache/Cache.java
  4. +137 −0 core/src/main/java/org/axonframework/cache/EhCacheAdapter.java
  5. +142 −0 core/src/main/java/org/axonframework/cache/JCacheAdapter.java
  6. +68 −0 core/src/main/java/org/axonframework/cache/NoCache.java
  7. +7 −0 core/src/main/java/org/axonframework/cache/package-info.java
  8. +1 −1 core/src/main/java/org/axonframework/commandhandling/disruptor/CommandHandlerInvoker.java
  9. +2 −2 core/src/main/java/org/axonframework/commandhandling/disruptor/DisruptorConfiguration.java
  10. +0 −332 core/src/main/java/org/axonframework/common/NoCache.java
  11. +0 −12 core/src/main/java/org/axonframework/common/cache/CacheStrategy.java
  12. +0 −45 core/src/main/java/org/axonframework/common/cache/CacheUtils.java
  13. +0 −15 core/src/main/java/org/axonframework/common/cache/DefaultStrategy.java
  14. +0 −133 core/src/main/java/org/axonframework/common/cache/EhCacheStrategy.java
  15. +1 −1 ...src/main/java/org/axonframework/contextsupport/spring/JdbcSagaRepositoryBeanDefinitionParser.java
  16. +1 −1 .../src/main/java/org/axonframework/contextsupport/spring/JpaSagaRepositoryBeanDefinitionParser.java
  17. +2 −2 core/src/main/java/org/axonframework/eventsourcing/CachingEventSourcingRepository.java
  18. +8 −13 core/src/main/java/org/axonframework/eventsourcing/EventCountSnapshotterTrigger.java
  19. +6 −6 core/src/main/java/org/axonframework/saga/repository/CachingSagaRepository.java
  20. +3 −3 core/src/main/resources/org/axonframework/contextsupport/spring/axon-core-2.0.xsd
  21. +3 −3 core/src/main/resources/org/axonframework/contextsupport/spring/axon-core-2.1.xsd
  22. +5 −5 core/src/main/resources/org/axonframework/contextsupport/spring/axon-core-2.2.xsd
  23. +1 −1 core/src/test/java/org/axonframework/commandhandling/disruptor/CommandHandlerInvokerTest.java
  24. +4 −9 core/src/test/java/org/axonframework/common/NoCacheTest.java
  25. +0 −3 ...src/test/java/org/axonframework/contextsupport/spring/JdbcEventStoreBeanDefinitionParserTest.java
  26. +1 −1 ...test/java/org/axonframework/contextsupport/spring/JdbcSagaRepositoryBeanDefinitionParserTest.java
  27. +1 −1 .../test/java/org/axonframework/contextsupport/spring/JpaSagaRepositoryBeanDefinitionParserTest.java
  28. +9 −10 core/src/test/java/org/axonframework/eventsourcing/CachingEventSourcingRepositoryTest.java
  29. +5 −8 core/src/test/java/org/axonframework/eventsourcing/CachingRepositoryWithNestedUnitOfWorkTest.java
  30. +9 −35 core/src/test/java/org/axonframework/eventsourcing/EventCountSnapshotterTriggerTest.java
  31. +17 −22 core/src/test/java/org/axonframework/saga/repository/CachingSagaRepositoryTest.java
  32. +1 −1 core/src/test/resources/contexts/axon-namespace-support-context-jdbc.xml
  33. +16 −11 core/src/test/resources/contexts/axon-namespace-support-context.xml
  34. +6 −2 core/src/test/resources/contexts/disruptor-context.xml
  35. +1 −13 integrationtests/pom.xml
  36. +9 −12 ...org/axonframework/integrationtests/cache/{JCacheIntegrationTest.java → CacheIntegrationTest.java}
  37. +7 −5 integrationtests/src/test/resources/META-INF/spring/caching-repository-context.xml
View
@@ -110,7 +110,8 @@
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
- <version>0.5</version>
+ <version>1.0.0</version>
+ <optional>true</optional>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
@@ -131,18 +132,6 @@
</dependency>
<!-- Optional dependencies -->
- <dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache-jcache</artifactId>
- <version>1.5.0-0.5</version>
- <optional>true</optional>
- <exclusions>
- <exclusion>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
@@ -215,8 +204,8 @@
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
- <version>2.8.0</version>
- <scope>test</scope>
+ <version>2.8.1</version>
+ <optional>true</optional>
</dependency>
<dependency>
@@ -0,0 +1,55 @@
+package org.axonframework.cache;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Abstract implementation of the Cache interface which makes it easier to implement Adapters.
+ *
+ * @author Allard Buijze
+ * @since 2.1.2
+ */
+public abstract class AbstractCacheAdapter<L> implements Cache {
+
+ private final ConcurrentMap<EntryListener, L> registeredAdapters =
+ new ConcurrentHashMap<EntryListener, L>();
+
+ /**
+ * Creates an adapter for the given <code>cacheEntryListener</code>. The adapter must forward all incoming
+ * notifications to the respective methods on the <code>cacheEntryListener</code>.
+ *
+ * @param cacheEntryListener The listener to create an adapter for
+ * @return an adapter that forwards notifications
+ */
+ protected abstract L createListenerAdapter(EntryListener cacheEntryListener);
+
+ @Override
+ public void registerCacheEntryListener(EntryListener entryListener) {
+ final L adapter = createListenerAdapter(entryListener);
+ if (registeredAdapters.putIfAbsent(entryListener, adapter) == null) {
+ doRegisterListener(adapter);
+ }
+ }
+
+ @Override
+ public void unregisterCacheEntryListener(EntryListener entryListener) {
+ L adapter = registeredAdapters.remove(entryListener);
+ if (adapter != null) {
+ doUnregisterListener(adapter);
+ }
+ }
+
+ /**
+ * Unregisters the given <code>listener</code> with the cache
+ *
+ * @param listenerAdapter The listener to register with the cache
+ */
+ protected abstract void doUnregisterListener(L listenerAdapter);
+
+ /**
+ * Registers the given listener with the cache implementation
+ *
+ * @param listenerAdapter the listener to register
+ */
+ protected abstract void doRegisterListener(L listenerAdapter);
+}
@@ -0,0 +1,165 @@
+package org.axonframework.cache;
+
+/**
+ * Abstraction for a Caching mechanism. All Axon component rely on this abstraction, so that different
+ * providers can be plugged in. In future versions, this abstraction may be replaced with the <code>javax.cache</code>
+ * api, as soon as that api is final.
+ *
+ * @author Allard Buijze
+ * @since 2.1.2
+ */
+public interface Cache {
+
+ /**
+ * Returns an item from the cache, or <code>null</code> if no item was stored under that key
+ *
+ * @param key The key under which the item was cached
+ * @param <K> The type of key used
+ * @param <V> The type of value stored
+ * @return the item stored under the given key
+ */
+ <K, V> V get(K key);
+
+ /**
+ * Stores the given <code>value</code> in the cache, under given <code>key</code>. If an item already exists,
+ * it is updated with the new value.
+ *
+ * @param key The key under which to store the item
+ * @param value The item to cache
+ * @param <K> The type of key used
+ * @param <V> The type of value stored
+ */
+ <K, V> void put(K key, V value);
+
+ /**
+ * Stores the given <code>value</code> in the cache, under given <code>key</code>, if no element is yet available
+ * under that key. This operation is performed atomically.
+ *
+ * @param key The key under which to store the item
+ * @param value The item to cache
+ * @param <K> The type of key used
+ * @param <V> The type of value stored
+ * @return <code>true</code> if no value was previously assigned to the key, <code>false</code> otherwise.
+ */
+ <K, V> boolean putIfAbsent(K key, V value);
+
+ /**
+ * Removes the entry stored under given <code>key</code>. If no such entry exists, nothing happens.
+ *
+ * @param key The key under which the item was stored
+ * @param <K> The type of key used
+ * @return <code>true</code> if a value was previously assigned to the key and has been removed, <code>false</code>
+ * otherwise.
+ */
+ <K> boolean remove(K key);
+
+ /**
+ * Indicates whether there is an item stored under given <code>key</code>.
+ *
+ * @param key The key to check
+ * @param <K> The type of key
+ * @return <code>true</code> if an item is available under that key, <code>false</code> otherwise.
+ */
+ <K> boolean containsKey(K key);
+
+ /**
+ * Registers the given <code>cacheEntryListener</code> to listen for Cache changes.
+ *
+ * @param cacheEntryListener The listener to register
+ */
+ void registerCacheEntryListener(EntryListener cacheEntryListener);
+
+ /**
+ * Unregisters the previously registered <code>cacheEntryListener</code>.
+ *
+ * @param cacheEntryListener The listener to unregister
+ */
+ void unregisterCacheEntryListener(EntryListener cacheEntryListener);
+
+ /**
+ * Interface describing callback methods, which are invoked when changes are made in the underlying cache.
+ */
+ interface EntryListener {
+
+ /**
+ * Invoked when an entry has expired.
+ *
+ * @param key The key of the entry that expired
+ */
+ void onEntryExpired(Object key);
+
+ /**
+ * Invoked when an item was removed from the cache, either following an expiry, or by explicitly calling {@link
+ * org.axonframework.cache.Cache#remove(Object)}.
+ *
+ * @param key The key of the entry that was removed
+ */
+ void onEntryRemoved(Object key);
+
+ /**
+ * Invoked when an item has been updated.
+ *
+ * @param key The key of the entry that was updated
+ * @param value The new value of the entry
+ */
+ void onEntryUpdated(Object key, Object value);
+
+ /**
+ * Invoked when a new item has been added to the cache
+ *
+ * @param key The key of the entry that was added
+ * @param value The value of the entry
+ */
+ void onEntryCreated(Object key, Object value);
+
+ /**
+ * Invoked when an item was retrieved from the Cache
+ *
+ * @param key The key of the entry that was read
+ * @param value The value of the entry read
+ */
+ void onEntryRead(Object key, Object value);
+
+ /**
+ * Clone operation used by some Cache implementations. An implementation must implement {@link
+ * java.lang.Cloneable} to indicate it supports cloning.
+ *
+ * @return a copy of this instance
+ *
+ * @throws CloneNotSupportedException if cloning is not supported
+ * @see java.lang.Cloneable
+ */
+ Object clone() throws CloneNotSupportedException;
+ }
+
+ /**
+ * Adapter implementation for the EntryListener, allowing for overriding only specific callback methods.
+ */
+ class EntryListenerAdapter implements EntryListener {
+
+ @Override
+ public void onEntryExpired(Object key) {
+ }
+
+ @Override
+ public void onEntryRemoved(Object key) {
+ }
+
+ @Override
+ public void onEntryUpdated(Object key, Object value) {
+ }
+
+ @Override
+ public void onEntryCreated(Object key, Object value) {
+ }
+
+ @Override
+ public void onEntryRead(Object key, Object value) {
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+ }
+}
@@ -0,0 +1,137 @@
+package org.axonframework.cache;
+
+import net.sf.ehcache.CacheException;
+import net.sf.ehcache.Ehcache;
+import net.sf.ehcache.Element;
+import net.sf.ehcache.event.CacheEventListener;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Cache implementation that delegates all calls to an EhCache instance.
+ *
+ * @author Allard Buijze
+ * @since 2.1.2
+ */
+public class EhCacheAdapter extends AbstractCacheAdapter<CacheEventListener> {
+
+ private final Ehcache ehCache;
+ private final ConcurrentMap<EntryListener, CacheEventListenerAdapter> registeredAdapters =
+ new ConcurrentHashMap<EntryListener, CacheEventListenerAdapter>();
+
+ /**
+ * Initialize the adapter to forward all call to the given <code>ehCache</code> instance
+ *
+ * @param ehCache The cache instance to forward calls to
+ */
+ public EhCacheAdapter(Ehcache ehCache) {
+ this.ehCache = ehCache;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <K, V> V get(K key) {
+ final Element element = ehCache.get(key);
+ return element == null ? null : (V) element.getObjectValue();
+ }
+
+ @Override
+ public <K, V> void put(K key, V value) {
+ ehCache.put(new Element(key, value));
+ }
+
+ @Override
+ public <K, V> boolean putIfAbsent(K key, V value) {
+ return ehCache.putIfAbsent(new Element(key, value)) == null;
+ }
+
+ @Override
+ public <K> boolean remove(K key) {
+ return ehCache.remove(key);
+ }
+
+ @Override
+ public <K> boolean containsKey(K key) {
+ return ehCache.isKeyInCache(key);
+ }
+
+ @SuppressWarnings("ClassEscapesDefinedScope")
+ @Override
+ protected EhCacheAdapter.CacheEventListenerAdapter createListenerAdapter(EntryListener cacheEntryListener) {
+ return new EhCacheAdapter.CacheEventListenerAdapter(ehCache, cacheEntryListener);
+ }
+
+ @Override
+ protected void doUnregisterListener(CacheEventListener listenerAdapter) {
+ ehCache.getCacheEventNotificationService().unregisterListener(listenerAdapter);
+ }
+
+ @Override
+ protected void doRegisterListener(CacheEventListener listenerAdapter) {
+ ehCache.getCacheEventNotificationService().registerListener(listenerAdapter);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static class CacheEventListenerAdapter implements CacheEventListener, Cloneable {
+
+ private Ehcache ehCache;
+ private EntryListener delegate;
+
+ public CacheEventListenerAdapter(Ehcache ehCache, EntryListener delegate) {
+ this.ehCache = ehCache;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void notifyElementRemoved(Ehcache cache, Element element) throws CacheException {
+ if (cache.equals(ehCache)) {
+ delegate.onEntryRemoved(element.getObjectKey());
+ }
+ }
+
+ @Override
+ public void notifyElementPut(Ehcache cache, Element element) throws CacheException {
+ if (cache.equals(ehCache)) {
+ delegate.onEntryCreated(element.getObjectKey(), element.getObjectValue());
+ }
+ }
+
+ @Override
+ public void notifyElementUpdated(Ehcache cache, Element element) throws CacheException {
+ if (cache.equals(ehCache)) {
+ delegate.onEntryUpdated(element.getObjectKey(), element.getObjectValue());
+ }
+ }
+
+ @Override
+ public void notifyElementExpired(Ehcache cache, Element element) {
+ if (cache.equals(ehCache)) {
+ delegate.onEntryExpired(element.getObjectKey());
+ }
+ }
+
+ @Override
+ public void notifyElementEvicted(Ehcache cache, Element element) {
+ if (cache.equals(ehCache)) {
+ delegate.onEntryExpired(element.getObjectKey());
+ }
+ }
+
+ @Override
+ public void notifyRemoveAll(Ehcache cache) {
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public CacheEventListenerAdapter clone() throws CloneNotSupportedException {
+ CacheEventListenerAdapter clone = (CacheEventListenerAdapter) super.clone();
+ clone.ehCache = (Ehcache) ehCache.clone();
+ clone.delegate = (EntryListener) delegate.clone();
+ return clone;
+ }
+ }
+}
Oops, something went wrong.

0 comments on commit 596f783

Please sign in to comment.