'Getting exception when trying to add entry to PROTOSTREAM based cache in Wildfly
I want to use an Infinispan cache with a PROTOSTREAM marshaller in my app running on Wildfly 26.0.1.
I found the following post on StackOverflow, where both @MyKey_ and @Paul_Ferraro gave some excellent pointers on how to do it:
I got quite far with this, but at the moment I am experiencing a stumbling block where I am getting an exception when trying to add an entry to this cache.
Before getting into detail about the error, I just wanted to give an overview of the stuff I have done so far.
As mentioned in the above StackOverflow post, I created a module in which I defined the Initializer as well as the annotated Java entity. For this exercise I copy and pasted from the Book/Author example provided in the Infinispan documentation.
In creating this module I used the following Gradle dependency snippet:
dependencies {
annotationProcessor "org.infinispan.protostream:protostream-processor:4.4.1.Final"
implementation "org.infinispan.protostream:protostream-processor:4.4.1.Final"
implementation 'org.infinispan.protostream:protostream:4.4.1.Final'
}
When doing the gradle build to create the module jar, the annotationProcessor automatically adds the files to META-INF, which @Paul_Ferraro mentioned.
I then copied the generated module to the modules folder in Wildfly, and added a module.xml, which looked as follows:
<module name="za.co.company" xmlns="urn:jboss:module:1.9">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<resource-root path="my-module.jar"/>
</resources>
<dependencies>
<module name="org.infinispan.protostream"/>
<module name="org.jboss.logging"/>
</dependencies>
</module>
I found that the dependencies tag were necessary, otherwise you get some link errors when Wildfly tries to load your module.
Next, I have added the following snippet to the Infinispan subsystem in my standalone-full-ha.xml:
<cache-container name="company" statistics-enabled="true" marshaller="PROTOSTREAM" modules="org.wildfly.clustering.infinispan.spi za.co.company">
<transport/>
<replicated-cache name="author">
<off-heap-memory size="1"/>
</replicated-cache>
</cache-container>
I can confirm that Wildfly indeed loads my module specified.
Now, I used the following code to test the Cache:
package za.co.company;
import org.jboss.ejb3.annotation.TransactionTimeout;
import za.co.company.Author;
import javax.annotation.Resource;
import javax.ejb.*;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@AccessTimeout(value = 1, unit = TimeUnit.HOURS)
@TransactionTimeout(value = 1, unit = TimeUnit.HOURS)
public class BookCache {
@Resource(lookup="java:jboss/infinispan/cache/company/author")
private Map<String, Author> cache2;
@Schedule(hour = "*", minute = "*/1", persistent = false)
void testCache() {
cache2.put("entry1", new Author("Johan", "Steenkamp"));
}
}
When trying to add the entry to the Cache, I get the following exception:
Caused by: org.infinispan.commons.marshall.MarshallingException: ISPN000615: Unable to unmarshall 'za.co.company.Author' as a marshaller is not present in the user or global SerializationContext
at [email protected]//org.infinispan.encoding.protostreamtranscoder.getctxformarshalling(protostreamtranscoder.java:198)
at [email protected]//org.infinispan.encoding.protostreamtranscoder.marshall(protostreamtranscoder.java:127)
at [email protected]//org.infinispan.encoding.protostreamtranscoder.transcode(protostreamtranscoder.java:68)
at [email protected]//org.infinispan.encoding.dataconversion.tostorage(dataconversion.java:227)
at [email protected]//org.infinispan.cache.impl.encodercache.valuetostorage(encodercache.java:105)
at [email protected]//org.infinispan.cache.impl.encodercache.put(encodercache.java:698)
at [email protected]//org.infinispan.cache.impl.abstractdelegatingcache.put(abstractdelegatingcache.java:449)
at deployment.Company.war//za.co.company.BookCache.testCache(BookCache.java:22)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImp
In order to debug this issue, I have added some source files from Wildfly and Infinispan, just to get an idea what was happening behind the scenes. I was debugging this in debug mode in IntelliJ, and encountered a couple of things that was strange.
Firstly, I added a breakpoint at the cach2.put line, and evaluated the following:
cache2.getCacheManager().getCacheManagerConfiguration().cacheContainer().serialization.marshaller()
In the results I spotted a field marshallersByClass. This was a HashMap and I could see an entry with Author.class as the key for the entry.
I also placed a breakpoint within the following method of ProtostreamTranscoder.java:
private ImmutableSerializationContext getCtxForMarshalling(Object o) {
Class<?> clazz = o instanceof Class<?> ? (Class<?>) o : o.getClass();
if (isWrappedMessageClass(clazz) || ctxRegistry.getUserCtx().canMarshall(clazz))
return ctxRegistry.getUserCtx();
if (ctxRegistry.getGlobalCtx().canMarshall(clazz))
return ctxRegistry.getGlobalCtx();
throw logger.marshallerMissingFromUserAndGlobalContext(o.getClass().getName());
}
The weird thing here is that evaluating both ctxRegistry.getUserCtx() and ctxRegistry.getGlobalCtx() and returns a HashMap that doesn't faintly resembles the HashMap I mentioned earlier.
Another thing I can also confirm is that my Initializer get registered in the class org.infinispan.protostream.impl.SerializationContextImpl upon startup via the method registerMarshaller, which adds it to the field marshallersByClass of that class.
However, when I do cache.put, it seems that a total different list of marshallers is used, to try and find my Marshaller.
Can someone try and point me in the right direction?
Thanks
Solution 1:[1]
I think the problem is that the 'za.co.company.Author' is resolving against your deployment module rather than your custom 'za.co.company' module. Your application should not bundle this class (nor include its jar), but instead include a dependency on the 'za.co.company' module. e.g.
/META-INF/MANIFEST.MF:
Dependencies: za.co.company services
N.B. This use case should be much simpler in WildFly 27. You will be able to marshall custom key/value types using ProtoStream without the need to bundle these types within a global module. See: https://github.com/wildfly/wildfly/pull/15397
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |