Does axon cache support hazelcast cache?

Hi

Currently the application on which i am working is using axon framework. I want to use caching for event repo and saga repo? And it requires cache configuration.?
Does AXON supports hazelcast cache as application using only this cache.?

Thanks in advance.

-Deepak Gupta

Hi Deepak,

Hazelcast cache implementations are not provided out of the box. If I remember well JCache and EHCache are provided.
Writing you own implementation should be fairly straight forward.
That being said, why is it you want to cache with Hazelcast?

Regards,
Benoît

Hi Benoît,

I am new to AXON framework. And my work using hazelcast configuration for cache.

Thanks

Hi Deepak,

Axon Framework has a JCache adapter, like Benoît mentioned: http://www.axonframework.org/apidocs/3.0/org/axonframework/common/caching/JCacheAdapter.html

Now I don’t now anything about Hazelcast, but it seems like it can function as a JCache provider: https://hazelcast.com/use-cases/caching/jcache-provider/

Hi Frans

I am trying to do the same in Spring Boot. But still no luck yet. Could you please guide me for the same?

Thanks
Deepak Gupta

Hi Deepak,

I haven’t tried it myself, but looking at the docs it should work like this:

Assuming you have a Spring Boot application, make sure you include Hazelcast as a dependency: https://hazelcast.org/download/
Create a Hazelcast configuration file http://docs.hazelcast.org/docs/latest/manual/html-single/
Assuming Hazelcast is on the classpath AND you have the config file, Spring Boot should automatically instantiate a HazelcastInstance https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-hazelcast.html
If this works, you can create a bean factory method that creates an Axon cache based on a JCache based on the HazelcastInstance (see http://docs.hazelcast.org/docs/latest-development/manual/html/Hazelcast_JCache/Integrating_JCache_with_Hazelcast_Instance.html), something like:

@Bean
org.axonframework.common.caching.Cache cache(HazelcastInstance hazelCastInstance) {

return new org.axonframework.common.caching.JCacheAdapter(hazelCastInstance.getCache(“myAxonCache”);
}

In order to make this do anything useful, you’ll still need to configure your repos to use a cache, for instance like:

@Bean
public Repository dummyAggregateRepository(EventStore eventStore, Cache cache) {
return new CachingEventSourcingRepository<>(new GenericAggregateFactory<>(DummyAggregate.class), eventStore, cache);
}

Does this help?

Cheers,

Frans van Buul

Hi Frans

Tried below suggestion:

Yes your assumption is correct. I am using Spring Boot.
To configure the CachingEventSourcingRepository, one part is to provide provide the cache part.

Before configuration:

It has configuration class something like this:

@Configuration
@EnablingCaching
public class XYZHazelcastConfiguration{
// add map config for different caches into Config part of Hazelcast.
}

Below are the dependency I am using it for Hazelcast

com.hazelcast hazelcast-spring com.hazelcast hazelcast-cloud ${hazelcast.version}

Hazelcast was working fine.

After configuration:

I suppose that it requires jcache dependency. So added below dependency

javax.cache cache-api

After this spring boot application without any error, but it is not able to find the previous cache that was working.

Even i tried this also

@Bean
org.axonframework.common.caching.Cache cache(HazelcastInstance hazelCastInstance) {
return new org.axonframework.common.caching.JCacheAdapter(hazelCastInstance.getCache(“myAxonCache”);
}

HazelcastInstance class does not have any such method like getCache.
I think that we need to get the cache from jcache manager instead of getting it from instance. But tried this, it is not working for me. It does not able to find the cache.
I also checked the example of axon trader and axon bank. Both using CachingEventSourcingRepository. But it was using default axon cache.

Thanks
Deepak Gupta

Hi Deepak,

I had another look at this, seem this this Hazelcast doc page http://docs.hazelcast.org/docs/latest-development/manual/html/Hazelcast_JCache/Integrating_JCache_with_Hazelcast_Instance.html says that HazelcastInstance has a getCache method but in reality it doesn’t.

However HazelcastInstance has a method http://docs.hazelcast.org/docs/3.8.3/javadoc/com/hazelcast/core/HazelcastInstance.html#getCacheManager– which returns an ICacheManager which has a getCache method that returns an object implementing JCache.

So how about something like

@Bean
org.axonframework.common.caching.Cache cache(HazelcastInstance hazelCastInstance) {

return new org.axonframework.common.caching.JCacheAdapter(hazelCastInstance.getCacheManager().getCache(“myAxonCache”));
}

?

Cheers,

Frans van Buul

Hi Frans

Thanks Frans, after adding the updated version of hazelcast 3.8.3, i am able to see this method cacheManager.

Along with this, to get JCacheConfiguration, i have added the below dependency.

javax.cache cache-api 1.0.0

Spring boot has started throwing me this error. I think this is not related to AXON Framework. But anyways i am trying to figure it this error.

java.lang.IllegalArgumentException: Cannot find cache named ‘xyzCache’ for Builder throws caches=[xyzCache] | key=’’ | keyGenerator=’’ | cacheManager=’’ | cacheResolver=’’ | condition=’’ | unless=’’ | sync=‘false’
at org.springframework.cache.interceptor.AbstractCacheResolver.resolveCaches(AbstractCacheResolver.java:81)
at org.springframework.cache.interceptor.CacheAspectSupport.getCaches(CacheAspectSupport.java:242)
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.(CacheAspectSupport.java:675)
at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:255)
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.(CacheAspectSupport.java:581)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:327)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy153.getToken(Unknown Source)

Thanks
Deepak Gupta

Hi Deepak,

I guess the exception means that a cache must be created/defined before you can use it. Since this is not Axon related in any way, the documentation of Hazelcast should be able to help you here.

Cheers,

Allard

Hi

I am able to overcome the cache issue. But now I am facing the below issue. Any idea.

java.lang.IllegalArgumentException: The aggregate root is unknown. Is this entity properly registered as the child of an aggregate member?
at org.axonframework.common.Assert.isTrue(Assert.java:52)
at org.axonframework.common.Assert.notNull(Assert.java:76)
at org.axonframework.eventsourcing.AbstractEventSourcedEntity.apply(AbstractEventSourcedEntity.java:100)
at org.axonframework.eventsourcing.AbstractEventSourcedEntity.apply(AbstractEventSourcedEntity.java:86)

@Bean
@Scope(“prototype”)
public MyAggregateRoot myAggregateroot() {
return new MyAggregateRoot ();
}

@Bean
public AggregateFactory aggregateFactory() {
SpringPrototypeAggregateFactory aggregateFactory =
new SpringPrototypeAggregateFactory<>();
aggregateFactory.setPrototypeBeanName(“myAggregateRoot”);
return aggregateFactory;
}

@Bean
public Cache getCache(HazelcastInstance hazelcastInstance) {
return new JCacheAdapter(
hazelcastInstance.getCacheManager().getCache(“axonEventsRepoCache”));
}

@Bean
public EventSourcingRepository myRepository(
AggregateFactory aggregateFactory,
JpaEventStore eventStore, EventBus eventBus, Cache cache) {
CachingEventSourcingRepository cachingRepository =
new CachingEventSourcingRepository<>(aggregateFactory, eventStore);
cachingRepository.setCache(cache);
cachingRepository.setEventBus(eventBus);
return cachingRepository;
}

Thanks
Deepak Gupta

Hi Deepak,

Looking at this quickly, I’m suspecting a capitalization problem.

You have : aggregateFactory.setPrototypeBeanName(“myAggregateRoot”); (upper case R in Root)
And: public MyAggregateRoot myAggregateroot() (lower case R)

There are a couple of other thing I noticed about your code: you’re separately injection an EventBus and EventStore, but in Axon3, the EventStore is a specialization of the EventBus (a special kind of bus that also stores events) so you don’t have to configure those separately. Also, you use the Spring Prototype aggregate factory, which doesn’t seem necessary since all you’re doing is invoking the default constructor. So you may be able to use a simpler config like:

@Bean("eventBus")
public EventStore eventStore(EventStorageEngine storageEngine) {
    return new EmbeddedEventStore(storageEngine);
}
@Bean
public Repository<MyAggregateRoot> myAggregateRootRepository(EventStore eventStore, Cache cache) {
    return new CachingEventSourcingRepository<>(new GenericAggregateFactory<>(MyAggregateRoot.class), eventStore, cache);
}

One other thing that may be an issue: for Spring autodetection of aggregates to work, they need to be annoted with @org.axonframework.spring.stereotype.Aggregate, not @org.axonframework.commandhandling.model.AggregateRoot
That tends to be confusing.

Hope any of this works for you,

Thanks, Anyway i will try this and will let you know about the changes and its effects but application is using 2.4.6 version of AXON framework.

One more thing that i forgot to mentioned that if I run the command second time, it does n’t throw me this error.

Regards
Deepak Gupta

Hi

I see SpringAggregateFactory is of no use in application as we are invoking the default constructor.

Tried suggested changes but still the same. Detailed stack trace are:

java.lang.IllegalArgumentException: The aggregate root is unknown. Is this entity properly registered as the child of an aggregate member?
at org.axonframework.common.Assert.isTrue(Assert.java:52)
at org.axonframework.common.Assert.notNull(Assert.java:76)
at org.axonframework.eventsourcing.AbstractEventSourcedEntity.apply(AbstractEventSourcedEntity.java:100)
at org.axonframework.eventsourcing.AbstractEventSourcedEntity.apply(AbstractEventSourcedEntity.java:86)
at com.adsds.app.ad.core.MyEntity.method1(MyEntity.java:98)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.axonframework.common.annotation.MethodMessageHandler.invoke(MethodMessageHandler.java:85)
at org.axonframework.commandhandling.annotation.AggregateCommandHandlerInspector$EntityForwardingMethodMessageHandler.invoke(AggregateCommandHandlerInspector.java:224)
at org.axonframework.commandhandling.annotation.AggregateAnnotationCommandHandler$AggregateCommandHandler.handle(AggregateAnnotationCommandHandler.java:331)
at org.axonframework.commandhandling.annotation.AggregateAnnotationCommandHandler.handle(AggregateAnnotationCommandHandler.java:277)
at org.axonframework.commandhandling.DefaultInterceptorChain.proceed(DefaultInterceptorChain.java:63)
at org.axonframework.commandhandling.DefaultInterceptorChain.proceed(DefaultInterceptorChain.java:69)
at org.axonframework.commandhandling.SimpleCommandBus.doDispatch(SimpleCommandBus.java:127)
at org.axonframework.commandhandling.SimpleCommandBus.doDispatch(SimpleCommandBus.java:103)
at org.axonframework.commandhandling.SimpleCommandBus.dispatch(SimpleCommandBus.java:75)
at org.axonframework.commandhandling.distributed.jgroups.JGroupsConnector$MessageReceiver.processDispatchMessage(JGroupsConnector.java:415)
at org.axonframework.commandhandling.distributed.jgroups.JGroupsConnector$MessageReceiver.receive(JGroupsConnector.java:405)
at org.jgroups.JChannel.up(JChannel.java:768)

Entity Class

public class MyEntity extends AbstractAnnotatedEntity
implements Serializable {

private static final long serialVersionUID = 2015112400L;

public MyEntity() { }

@CommandHandler
public MyStatus joinMyGroup(JoinGroupCommand cmd) {
apply(new JoinGroupEvent(…));
}
}

Thanks
Deepak Gupta

Can you share your aggregate class as well?

Hi Allard,

Please find the aggregate snippet:

Note: This issue is coming when trying to call the entity first time. Second time, it runs the event successfully.

/**

  • The aggregate root entity for a group call.
    */
    public class MyAggregateRoot
    extends AbstractAnnotatedAggregateRoot {

private static final long serialVersionUID = 1234123412341234123L;

private static final Logger LOG =
LoggerFactory.getLogger(MyAggregateRoot.class);

// The group’s unique instance ID
@AggregateIdentifier
private String groupId;

@CommandHandlingMember
@EventSourcedMember
private MyEntity members;

// The group name
private String groupName;

private boolean ended;

/**

  • Creates a new group call aggregate root. Used for serialization only.
    */
    public MyAggregateRoot() {
    }

/**

  • Creates a new group call aggregate root in response to start group call commands.

Hi Deepak,

it seems you call apply() in the MyEntity’s constructor, but I can’t tell for sure. Entities should not do that, as their state is set by the event handler of their parent (in your case, GroupCallStartedEvent).

Allard

We are calling apply() in MyEntity’s methods not in the constructor.

MyEntity.java

public class MyEntity extends AbstractAnnotatedEntity
implements Serializable {

/**

  • Creates a new entity. Used for serialization only.
    */
    public MyEntity() {
    }

@CommandHandler
public JoinCommandStatus joinGroup(JoinGroupCommand cmd) {

// business logic

apply(new GroupJoinedEvent(…));

}
}

Hi Allard

Facing some different behavior here, i tried to run the application in debug mode( inside registerAggregateRoot of AbstractEventSourcedEntity), before making the another command to get fired. And after reaching this breakpoint, I moved to another command by doing a wait of 1-2 seconds hardly at this break point. I don’t see the below exception.

Any suggestion?

Thanks & Regards
Deepak Gupta

It was the same case of running the same command second time,it will run successfully.

Thanks
Deepak Gupta