Serialization

By default, a Cache<K,V> instance will serialize and deserialize object’s using the JDK’s ObjectInputStream and ObjectOutputStream. Note, this requires the stored objects to implement Java’s Serializable interface.

Cache<K,V>’s can be configured to use custom serialization and deserialization. To do this, implement the CacheSerializer and CacheDeserializer.

The following sample uses AES-256 bit encryption and decryption to demonstrate a custom serialization and deserialization implementation. The object being serialized is a “SimpleObject” that contains a string, an int, and a long. The CacheSerializer implementation will turn a SimpleObject into a byte array and then encrypt that byte array using AES256-bit encryption. The CacheDeserializer implementation will decrypt a byte array, and then hydrate a SimpleObject instance.

package com.scaleout.client.samples.caching;

import com.scaleout.client.GridConnection;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.Key;

public class SerializationSample {
        // this is a sample 32-byte key, *** do not use this ***
        static final byte[] SECRET_KEY_VALUE = new byte[] {
                        'T', 'H', 'I', 'S', 'I', 'S', 'N', 'O', 'T', 'A',
                        'S', 'E', 'C', 'U', 'R', 'E', 'K', 'E', 'Y', '!',
                        '!', '!', '!', '!', '!', '!', '!', '!', '!', '!', '!', '!'};

        static final String AES_ALGORITHM = "AES";

        static class SimpleObject {
                String simpleString;
                int simpleInt;
                long simpleLong;
                public SimpleObject(String str, int i, long l) {
                        simpleString = str;
                        simpleInt = i;
                        simpleLong = l;
                }

                @Override
                public String toString() {
                        return "SimpleObject{" +
                                        "simpleString='" + simpleString + '\'' +
                                        ", simpleInt=" + simpleInt +
                                        ", simpleLong=" + simpleLong +
                                        '}';
                }
        }

        /**
         * This class turns a SimpleObject into an AES-256 bit encrypted byte array
         */
        static class SimpleObjectEncryptingSerializer extends CacheSerializer<SimpleObject> {
                @Override
                public byte[] serialize(SimpleObject simpleObject) throws SerializationException {
                        byte[] strAsBytes = simpleObject.simpleString.getBytes(StandardCharsets.UTF_8);
                        byte[] intAsBytes = ByteBuffer.allocate(4).putInt(simpleObject.simpleInt).array();
                        byte[] longAsBytes = ByteBuffer.allocate(8).putLong(simpleObject.simpleLong).array();
                        byte[] strLengthAsBytes = ByteBuffer.allocate(4).putInt(strAsBytes.length).array();
                        byte[] simpleObjAsBytes = ByteBuffer.allocate(strAsBytes.length + intAsBytes.length + longAsBytes.length + strLengthAsBytes.length)
                                        .put(strLengthAsBytes)
                                        .put(strAsBytes)
                                        .put(intAsBytes)
                                        .put(longAsBytes)
                                        .array();
                        // encrypt the byte[]
                        try {
                                Key key = new SecretKeySpec(SECRET_KEY_VALUE, AES_ALGORITHM);
                                Cipher c = Cipher.getInstance(AES_ALGORITHM);
                                c.init(Cipher.ENCRYPT_MODE, key);
                                return c.doFinal(simpleObjAsBytes);
                        } catch (Exception e) {
                                throw new SerializationException(e.getMessage());
                        }
                }
        }

        /**
         * This class decrypts an AES-256 bit encrypted byte array into a SimpleObject
         */
        static class SimpleObjectDecryptingDeserializer extends CacheDeserializer<SimpleObject> {
                @Override
                public SimpleObject deserialize(byte[] bytes) throws DeserializationException {
                        try {
                                // decrypt the byte[]
                                Key key = new SecretKeySpec(SECRET_KEY_VALUE, AES_ALGORITHM);
                                Cipher c = Cipher.getInstance(AES_ALGORITHM);
                                c.init(Cipher.DECRYPT_MODE, key);

                                // read each value from the byte[]
                                String str = null;
                                int i = 0;
                                long l = 0L;
                                ByteBuffer buffer = ByteBuffer.wrap(c.doFinal(bytes));
                                int strLen = buffer.getInt();
                                byte[] strAsBytes = new byte[strLen];
                                buffer.get(strAsBytes);
                                str = new String(strAsBytes, StandardCharsets.UTF_8);
                                i = buffer.getInt();
                                l = buffer.getLong();
                                return new SimpleObject(str, i, l);
                        } catch (Exception e) {
                                throw new DeserializationException(e.getMessage());
                        }

                }
        }

        public static void main(String[] args) throws Exception {
                GridConnection connection = GridConnection.connect("bootstrapGateways=server1:721,server2:721;");
                Cache<String, SimpleObject> cache = new CacheBuilder<String, SimpleObject>(connection, "example", String.class)
                                // supply the custom serializer to the builder
                                .customSerialization(new SimpleObjectEncryptingSerializer(), new SimpleObjectDecryptingDeserializer())
                                .build();
                SimpleObject object = new SimpleObject("foo", 5, 23L);
                CacheResponse<String, SimpleObject> response = cache.add("my_simple_obj_key", object);
                if(response.getStatus() == RequestStatus.ObjectAdded) {
                        System.out.println("Object encrypted with AES-256 bit encryption and added to cache.");
                } else {
                        System.out.println("Unexpected request status: " + response.getStatus());
                }

                response = cache.read("my_simple_obj_key");
                if(response.getStatus() == RequestStatus.ObjectRetrieved) {
                        System.out.println("Object retrieved from cache and decrypted.");
                } else {
                        System.out.println("Unexpected request status: " + response.getStatus());
                }
        }
}