Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonJacksonCodec is not working on the entity level cache #163

Open
abdulwhd964 opened this issue Dec 5, 2024 · 6 comments
Open

JsonJacksonCodec is not working on the entity level cache #163

abdulwhd964 opened this issue Dec 5, 2024 · 6 comments

Comments

@abdulwhd964
Copy link

Hi

im working on second level cache using redisson it works fine i can see the data in redis and thanks to redisson/redisson#6309 i can able to retrieve the data and set ttl as well but i have requirement where i have to store the entity as an JSON instead of Hash, for Eg:

@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "employee")
public Class Employee {

}

i could see in the redis

HGETALL declarationTypeConfig

hash format which is not an human readable so i want to store the data into redis as json so that i can view the data

as per documentation i have added codec in the configuration in yaml file as below

codec: !<org.redisson.codec.JsonJacksonCodec> {}

but im getting an error the below ..

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of org.hibernate.cache.spi.entry.StandardCacheEntryImpl (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: REDACTED (StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION disabled); line: 1, column: 66]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1887) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1375) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1508) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:220) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:170) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:136) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:240) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializerNR.deserializeWithType(UntypedObjectDeserializerNR.java:112) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4905) ~[jackson-databind-2.17.2.jar:2.17.2]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3885) ~[jackson-databind-2.17.2.jar:2.17.2]
at org.redisson.codec.JsonJacksonCodec$2.decode(JsonJacksonCodec.java:99) ~[redisson-3.20.1.jar:3.20.1]
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:393) ~[redisson-3.20.1.jar:3.20.1]
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:205) ~[redisson-3.20.1.jar:3.20.1]
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:144) ~[redisson-3.20.1.jar:3.20.1]
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:120) ~[redisson-3.20.1.jar:3.20.1]
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:530) ~[netty-codec-4.1.113.Final.jar:4.1.113.Final]
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366) ~[netty-codec-4.1.113.Final.jar:4.1.113.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) ~[netty-codec-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.113.Final.jar:4.1.113.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.113.Final.jar:4.1.113.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.113.Final.jar:4.1.113.Final]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]

@abdulwhd964
Copy link
Author

@mrniko

could you please provide your suggestion on how to achieve this

@mrniko
Copy link

mrniko commented Dec 5, 2024

You need to extends JsonJacksonCodec to serialize StandardCacheEntryImpl and other internal hibernate objects

@abdulwhd964
Copy link
Author

You need to extends JsonJacksonCodec to serialize StandardCacheEntryImpl and other internal hibernate objects

any documentation or sample code reference ?

@abdulwhd964
Copy link
Author

@mrniko

i have added the code below based on several documentation, just wanted to confirm with you

this is redissonClient

@bean
public RedissonClient redissonClient() throws IOException {
// Load the YAML configuration file from the classpath
ClassPathResource resource = new ClassPathResource("redisson.yaml");
String yamlConfig = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()), StandardCharsets.UTF_8);

    // Create Redisson config from YAML
    Config config = Config.fromYAML(yamlConfig);
    ObjectMapper objectMapper = new ObjectMapper();
    config.setCodec(new CustomJsonJacksonCodec(objectMapper));
    RedissonClient redisson = Redisson.create(config);
    // Create and return the Redisson client
    return redisson;
}

CustomJsonJacksonCodec which extends JsonJacksonCodec

public class CustomJsonJacksonCodec extends JsonJacksonCodec {

public CustomJsonJacksonCodec(ObjectMapper objectMapper) {
    super(objectMapper);
    SimpleModule module = new SimpleModule();
    module.addDeserializer(StandardCacheEntryImpl.class, new CacheEntryDeserializer());  // Custom Deserializer
    objectMapper.registerModule(module);
}

}

CacheEntryDeserializer ===

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.hibernate.cache.spi.entry.StandardCacheEntryImpl;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

public class CacheEntryDeserializer extends StdDeserializer {

public CacheEntryDeserializer() {
    super(StandardCacheEntryImpl.class);
}

// Method to deserialize the disassembledState JSON array into a Serializable[] array
public static Serializable[] deserializeDisassembledState(JsonNode disassembledStateNode) {
    List<Serializable> stateList = new ArrayList<>();
    if (disassembledStateNode.isArray()) {
        for (JsonNode elementNode : disassembledStateNode) {
            // Deserialize each element based on its type
            if (elementNode.isTextual()) {
                stateList.add(elementNode.asText());
            } else if (elementNode.isNumber()) {
                stateList.add(elementNode.asLong());
            } else if (elementNode.isBoolean()) {
                stateList.add(elementNode.asBoolean());
            } else {
                // Handle complex types or unsupported types here (e.g., set to null)
                stateList.add(null);
            }
        }
    }
    return stateList.toArray(new Serializable[0]);
}

@Override
public StandardCacheEntryImpl deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
    JsonNode node = jp.getCodec().readTree(jp);

    // Extract the fields from the JSON node
    String subclass = node.get("subclass").asText();
    JsonNode versionNode = node.get("version");
    JsonNode disassembledStateNode = node.get("disassembledState");

    // Deserialize disassembledState
    Serializable[] disassembledState = deserializeDisassembledState(disassembledStateNode);

    // Deserialize version (handling potential types for version)
    Object version = null;
    if (versionNode != null && !versionNode.isNull()) {
        if (versionNode.isTextual()) {
            version = versionNode.asText();
        } else if (versionNode.isNumber()) {
            version = versionNode.asLong();  // Adjust as needed for version type
        }
    }

    // Use reflection to create a new instance of StandardCacheEntryImpl
    try {
        Constructor<StandardCacheEntryImpl> constructor = StandardCacheEntryImpl.class
                .getDeclaredConstructor(Serializable[].class, String.class, Object.class);
        constructor.setAccessible(true);
        return constructor.newInstance(disassembledState, subclass, version);
    } catch (Exception e) {
        throw new IOException("Failed to instantiate StandardCacheEntryImpl using reflection", e);
    }
}

}

could you please verify the above configuration is ok because still im getting the exception

@abdulwhd964
Copy link
Author

Hello @mrniko i have added the above configuration just like you have suggested but im getting the same exception, any help could be appreciated.

@abdulwhd964
Copy link
Author

@mrniko could you please assist me here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants