I have a existing Spring Boot 3 application that runs single node with Caffeine cache and the @Cacheable
annotations.
The app runs fine so far, as Caffeine doesn't care about the POJO structure or format, it will just store the keys and the values in memory. Remember that, in the Spring Cache world, keys are often objects, e.g. a cached Person getPerson(String firstName, String lastName)
uses a compound key.
The application is business-critical with regards to performance. It operates in payment industry so I'll dedicate a few neurons to security considerations.
Okay, now it's time to implement Red Hat Data Grid on Openshift. So I fired up a local instance of Infinispan 14 and hoped that the auto-configuration did some magic.
Here is the infinispan.yaml
infinispan:
local-cache-configuration:
name: base-template
expiration:
lifespan: 1800000
encoding:
media-type: application/x-java-object #We'll talk about this
cache-container:
name: default
caches:
ipe.my-ipe-cache: # Consider it is referenced in @Cacheable(value = CACHE_KEY)
local-cache:
configuration: base-template
# Consider 24 identical nodes
server:
interfaces:
- name: public
inet-address:
value: 127.0.0.1
socket-bindings:
default-interface: public
port-offset: 0
socket-binding:
- name: default
port: 11222
- name: memcached
port: 11221
endpoints:
endpoint:
socket-binding: default
security-realm: default
security:
security-realms:
- name: default
properties-realm:
groups-attribute: Roles
user-properties:
path: users.properties
group-properties:
path: groups.properties
And application.yaml
infinispan:
embedded:
enabled: false
remote:
enabled: true
server-list: "localhost:11222"
auth-username: admin
auth-password: admin
# marshaller: org.infinispan.commons.marshall.UTF8StringMarshaller
# marshaller: org.infinispan.commons.marshall.ProtoStreamMarshaller
marshaller: org.infinispan.commons.marshall.JavaSerializationMarshaller
java-serial-allow-list: "com\\.acme\\.ipe\\.(.*)\\.dto\\.\\w+"
Now the problem lies in configuring the correct marshalling, and I am stuck at this
Anyway it didn't work
org.infinispan.client.hotrod.exceptions.HotRodClientException: org.infinispan.commons.CacheException: ISPN000936: Class 'com.acme.ipe.core.data.dto.ChannelDto' blocked by deserialization allow list. Adjust the configuration serialization allow list regular expression to include this class.
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2096)
at org.infinispan.client.hotrod.impl.Util.await(Util.java:52)
at org.infinispan.client.hotrod.impl.RemoteCacheSupport.put(RemoteCacheSupport.java:196)
at org.infinispan.client.hotrod.impl.RemoteCacheSupport.put(RemoteCacheSupport.java:186)
at org.infinispan.spring.common.provider.SpringCache.put(SpringCache.java:145)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:87)
java.lang.IllegalArgumentException: No marshaller registered for object of Java type com.acme.ipe.core.data.dto.ChannelDto : ChannelDto(id=1, creationDate=2017-11-06T06:40:06, ...)
at org.infinispan.protostream.impl.SerializationContextImpl.getMarshallerDelegate(SerializationContextImpl.java:503)
at org.infinispan.protostream.WrappedMessage.writeMessage(WrappedMessage.java:281)
at org.infinispan.protostream.WrappedMessage.write(WrappedMessage.java:242)
at org.infinispan.protostream.ProtobufUtil.toWrappedByteBuffer(ProtobufUtil.java:153)
at org.infinispan.commons.marshall.ImmutableProtoStreamMarshaller.objectToBuffer(ImmutableProtoStreamMarshaller.java:55)
at org.infinispan.commons.marshall.AbstractMarshaller.objectToByteBuffer(AbstractMarshaller.java:70)
at org.infinispan.client.hotrod.marshall.MarshallerUtil.obj2bytes(MarshallerUtil.java:117)
at org.infinispan.client.hotrod.DataFormat$DataFormatImpl.valueToBytes(DataFormat.java:92)
at org.infinispan.client.hotrod.DataFormat.valueToBytes(DataFormat.java:211)
at org.infinispan.client.hotrod.impl.RemoteCacheImpl.valueToBytes(RemoteCacheImpl.java:610)
at org.infinispan.client.hotrod.impl.RemoteCacheImpl.putAsync(RemoteCacheImpl.java:306)
at org.infinispan.client.hotrod.impl.RemoteCacheSupport.put(RemoteCacheSupport.java:196)
at org.infinispan.client.hotrod.impl.RemoteCacheSupport.put(RemoteCacheSupport.java:186)
at org.infinispan.spring.common.provider.SpringCache.put(SpringCache.java:145)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:87)
I can set the media-type
of the cache to application/json
but I can't find any marshaller in my classpath that supports
Is it possible to configure Infinispan to marshal/unmarshal objects to JSON which would solve all possible security and compatibility issues?
Othwerise, is it possible not to manually annotate all existing DTOs with Protobuf annotations, e.g. by using some IDE or Maven plugin that will likely generate a mapping for you?
And as last resort for today, how can I overcome the error ISPN000936
shown a few lines ago? I did add the allow list to my Spring application!
The problem you are having with Java Serialization is that you are telling the server to use encoding: application/x-java-object
. This means that the server will want to have the bytecode of your classes to be able to interpret the data and possibly transcode it. This is also the reason for the org.infinispan.commons.CacheException: ISPN000936: Class 'com.acme.ipe.core.data.dto.ChannelDto' blocked by deserialization allow list.
error you are getting: it's coming from the server, not the client.
Use application/octet-stream
on the server to avoid this. Also, JBoss Marshalling will offer better performance than plain Java serialization.
Protocol Buffers are obviously the ideal choice, but they do require a lot of legwork if adapting a lot of existing code.
As for JSON, you could implement a client JSONMarshaller
using Jackson that does the marshalling/unmarshalling. This is something that could also be contributed upstream.