- Use Hazelcast IMap in a Java EE container
- Use MapLoader to load into a IMap with JPA
- As far as possible, use CDI
Payara comes with a Hazelcast, but solving this tasks may not be so straightforward, and I will try to explain why.
As usual I use a simple blog Post RESTful service as an example.
Using Hazlecast in Payara
Even if Payara comes with an embedded Hazelcast, I will not use it by letting Payara start it, and the reason is what I found when playing around with EntryListener. I will however use the Hazelcast that is provided, but will not activate it as written here: https://github.com/payara/Payara/wiki/Hazelcast-%28Payara-4.1.153%29 but more with ideas as written here: http://blog.modernjava.info/2014/10/using-hazelcast-as-jcache-provider-with.html.
To start with, this is what we will need in the pom.xml,
I am using Hazelcast 3.5 since that is the one that comes with Payara 4.153.
To fire up Hazelcast we will use an annotation and a provider so we can use CDI where we want to use stuff from Hazelcast.
The reason for doing like this is that I noticed by playing around with EntryListener that by letting Payara firing up Hazelcast I cannot really add listeners, since Hazelcast then know nothing about my classes (resulting in ClassNotFoundException), so to resolve this we need to provide the class loader from our web-application to Hazelcast. To do this we need to start it "manually" and provide the class loader so Hazelcast knows about them. You can also see we configure Hazelcast with a MapLoader, more on that later.
REST-resourceI will not share the full code since you can find it on Bitbucket. In PostResource we use CDI to get hold of the Hazelcast, it will initialise on first use, so if no pre-warming occurs (like using a startup bean) it will take 5-10 seconds.
And use it
For the creation of a Post we can use a trick to load it in the map by trying to get it after the save. That is we don't put it in the map directly and use a MapStore to save it (we don't know the id until after the persist). But since this is about using MapLoader, we will use that and not put it (as indicated in the comments)
MapLoader using JPAActually, I am not using it directly from the MapLoader, but injecting a @Statless bean from it, that does the actual JPA stuff.
As the MapLoader lives in Hazelcast and is created by it, we cannot use @Inject or even make it a managed bean (well we can, but it will not help since it is not using the jave ee container to handle it). But since CDI 1.1 we can inject using the CDI-class.
This is how the map loader I create looks like:
Notice the code to inject the PostRepository (which is the bean using JPA with an EntityManager). This will work since Hazelcast is loaded from the container with our classes.
Some notesI have implemented loadAllKeys() and loadAll(...), this is probably fine since I do not have so many posts, but for a huge database and if you do not want them all in memory, you may leave them empty and return null for keys or you will end up waiting for a while. I played with a criteria search on the map
and found out that by doing that, Hazelcast will try to load all keys from the database before it searcher memory.
However, this is done only once, if you add items to the database, it will not get loaded if you do not do anything to tell the map, as you have seen in the "@POST" or put it there yourself, subsequent searches on the map will not try to load items not there from the first loadAll.
Payara does not really matter in the context, other than having a Hazelcast and being a Java EE container. But I also use it since it have a default datasource for Derby, so I kind of used that to avoid setting up a database, and load a few posts to get started.If you use another container, you may have to change the hazelnuts dependency to be "compile" instead of "provided" if that container donate have it. You may also need to set up a datasource.
The code can be found here: [https://bitbucket.org/pejomstd/hazelblog]