Ariel Netz
Ariel Netz

Reputation: 111

EhCache disk persistence - persist an arbitrary object

I want to use EhCache to persist objects on the disk, so in the next time I will run my app it will load from it. In their documentation, there is the following example for using PersistentCacheManager class to do so:

PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder().with(CacheManagerBuilder.persistence(new File(getStoragePath(), "myData"))).withCache("persistent-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder().disk(10, MemoryUnit.MB, true))).build(true);
persistentCacheManager.close();

The example works fine, but the problem for me if that the CacheConfigurationBuilder object defines the classes of the key and value hard coded to Long and String. I would like to persist instances of arbitraty classes and not predefine which ones. Also, the CacheConfigurationBuilder wouldn't take Object.class or HashMap.class, for example.

How can I use EhCache to persist on disk an arbitrary instance class?

Upvotes: 0

Views: 1413

Answers (1)

Henri
Henri

Reputation: 5731

Ehcache can take any key or value. By default, it requires that they must be serializable. Otherwise, it can't know how to store it offheap or on disk.

HashMap is serializable, so you won't get a warning from it. Map is not. So you would get a warning but it will still works if the implementation is in fact serializable.

However, if you really do store an object the isn't serializable, you get an exception indeed. The solution is to specify a key and/or a value serializer telling Ehcache how to store whatever you want. The full documentation is there.

Now let's look at an example. You a class MyClass that you want to cache.

public class MyClass {
  private String value;

  public MyClass(String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }

  @Override
  public boolean equals(Object o) {
    if(this == o) { return true; }
    if(o == null || getClass() != o.getClass()) { return false; }
    MyClass myClass = (MyClass) o;
    return Objects.equals(value, myClass.value);
  }

  @Override
  public int hashCode() {
    return value != null ? value.hashCode() : 0;
  }
}

And a test making sure it works.

@Test
public void test() {
  try(CacheManager persistentCacheManager =
        newCacheManagerBuilder()
          .withCache("test-cache",
            newCacheConfigurationBuilder(Integer.class, MyClass.class,
              newResourcePoolsBuilder().offheap(1, MemoryUnit.MB)))
          .withSerializer(MyClass.class, MySerializer.class)
          .build(true)) {

    Cache<Integer, MyClass> cache = persistentCacheManager.getCache("test-cache", Integer.class, MyClass.class);
    cache.put(1, new MyClass("test"));
    MyClass actual = cache.get(1);
    assertEquals("test", actual.getValue());
  }

}

Your serializer will look like this:

public class MySerializer implements Serializer<MyClass> {

  public MySerializer(ClassLoader unused) {
  }

  @Override
  public ByteBuffer serialize(MyClass object) throws SerializerException {
    try(ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oout = new ObjectOutputStream(bout)) {
      oout.writeUTF(object.getValue());
      oout.flush();
      return ByteBuffer.wrap(bout.toByteArray());
    } catch (IOException e) {
      throw new SerializerException(e);
    }
  }

  @Override
  public MyClass read(ByteBuffer binary) throws ClassNotFoundException, SerializerException {
    try(ObjectInputStream oin = new ObjectInputStream(new ByteBufferInputStream(binary))) {
        return new MyClass(oin.readUTF());
    } catch (IOException e) {
      throw new SerializerException(e);
    }
  }

  @Override
  public boolean equals(MyClass object, ByteBuffer binary) throws ClassNotFoundException, SerializerException {
    return object.equals(read(binary));
  }

}

Upvotes: 2

Related Questions