All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.ddth.commons.utils.SerializationUtils Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
package com.github.ddth.commons.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.nustaq.serialization.FSTConfiguration;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.pool.KryoCallback;
import com.esotericsoftware.kryo.pool.KryoFactory;
import com.esotericsoftware.kryo.pool.KryoPool;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.NullNode;
import com.github.ddth.commons.serialization.DeserializationException;
import com.github.ddth.commons.serialization.ISerializationSupport;
import com.github.ddth.commons.serialization.SerializationException;

/**
 * Serialization helper class.
 * 
 * 
    *
  • JSON serialization: use {@code com.fasterxml.jackson} library.
  • *
  • Binary serialization: 3 choices of API *
      *
    • {@code jboss-serialization} library (deprecated since v0.6.0!), or
    • *
    • {@code Kryo} library or
    • *
    • {@code FST} library
    • *
    *
  • *
* * @author Thanh Nguyen * @since 0.2.0 */ public class SerializationUtils { /*----------------------------------------------------------------------*/ /** * Serialize an object to byte array. * *

* If the target object implements {@link ISerializationSupport}, this * method calls its {@link ISerializationSupport#toBytes()} method; * otherwise FST library is used to serialize the object. *

* * @param obj * @return */ public static byte[] toByteArray(Object obj) { return toByteArray(obj, null); } /** * Serialize an object to byte array, with a custom class loader. * *

* If the target object implements {@link ISerializationSupport}, this * method calls its {@link ISerializationSupport#toBytes()} method; * otherwise FST library is used to serialize the object. *

* * @param obj * @param classLoader * @return */ public static byte[] toByteArray(Object obj, ClassLoader classLoader) { if (obj instanceof ISerializationSupport) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { return ((ISerializationSupport) obj).toBytes(); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } else { return toByteArrayFst(obj, classLoader); } } /** * Deserialize a byte array back to an object. * *

* If the target class implements {@link ISerializationSupport}, this method * calls its {@link ISerializationSupport#toBytes()} method; otherwise FST * library is used to serialize the object. *

* * @param data * @param clazz * @return */ public static T fromByteArray(byte[] data, Class clazz) { return fromByteArray(data, clazz, null); } /** * Deserialize a byte array back to an object, with custom class loader. * *

* If the target class implements {@link ISerializationSupport}, this method * calls its {@link ISerializationSupport#toBytes()} method; otherwise FST * library is used to serialize the object. *

* * @param data * @param clazz * @param classLoader * @return */ public static T fromByteArray(byte[] data, Class clazz, ClassLoader classLoader) { if (data == null) { return null; } if (ReflectionUtils.hasInterface(clazz, ISerializationSupport.class)) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { Constructor constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); T obj = constructor.newInstance(); ((ISerializationSupport) obj).fromBytes(data); return obj; } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) { throw new DeserializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } return SerializationUtils.fromByteArrayFst(data, clazz, classLoader); } /*----------------------------------------------------------------------*/ private static KryoPool kryoPool; static { KryoFactory factory = new KryoFactory() { public Kryo create() { Kryo kryo = new Kryo(); return kryo; } }; Queue queue = new LinkedBlockingQueue(100); kryoPool = new KryoPool.Builder(factory).queue(queue).softReferences().build(); } /** * Serialize an object to byte array. * *

* This method uses Kryo lib. *

* * @param obj * @return */ public static byte[] toByteArrayKryo(Object obj) { return toByteArrayKryo(obj, null); } /** * Serialize an object to byte array, with a custom class loader. * *

* This method uses Kryo lib. *

* * @param obj * @param classLoader * @return */ public static byte[] toByteArrayKryo(final Object obj, final ClassLoader classLoader) { if (obj == null) { return null; } return kryoPool.run(new KryoCallback() { @Override public byte[] execute(Kryo kryo) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { try (Output output = new Output(baos)) { kryo.setClassLoader(classLoader != null ? classLoader : oldClassLoader); // kryo.writeObject(output, obj); kryo.writeClassAndObject(output, obj); output.flush(); return baos.toByteArray(); } } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } }); } /** * Deserialize a byte array back to an object. * *

* This method uses Kryo lib. *

* * @param data * @return */ public static Object fromByteArrayKryo(byte[] data) { return fromByteArrayKryo(data, Object.class, null); } /** * Deserialize a byte array back to an object, with custom class loader. * *

* This method uses Kryo lib. *

* * @param data * @param classLoader * @return */ public static Object fromByteArrayKryo(byte[] data, ClassLoader classLoader) { return fromByteArrayKryo(data, Object.class, classLoader); } /** * Deserialize a byte array back to an object. * *

* This method uses Kryo lib. *

* * @param data * @param clazz * @return */ public static T fromByteArrayKryo(byte[] data, Class clazz) { return fromByteArrayKryo(data, clazz, null); } /** * Deserialize a byte array back to an object, with custom class loader. * *

* This method uses Kryo lib. *

* * @param data * @param clazz * @param classLoader * @return */ public static T fromByteArrayKryo(byte[] data, final Class clazz, final ClassLoader classLoader) { if (data == null) { return null; } return kryoPool.run(new KryoCallback() { @SuppressWarnings("unchecked") @Override public T execute(Kryo kryo) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { try (Input input = new Input(new ByteArrayInputStream(data))) { kryo.setClassLoader(classLoader != null ? classLoader : oldClassLoader); // return kryo.readObject(input, clazz); Object result = kryo.readClassAndObject(input); if (result != null && clazz.isAssignableFrom(result.getClass())) { return (T) result; } else { return null; } } catch (Exception e) { throw e instanceof DeserializationException ? (DeserializationException) e : new DeserializationException(e); } } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } }); } /*----------------------------------------------------------------------*/ private final static ObjectPool poolMapper = new GenericObjectPool( new BasePooledObjectFactory() { @Override public ObjectMapper create() throws Exception { return new ObjectMapper(); } @Override public PooledObject wrap(ObjectMapper objMapper) { return new DefaultPooledObject(objMapper); } }); static { GenericObjectPool pool = (GenericObjectPool) poolMapper; pool.setMaxIdle(1); pool.setMaxTotal(100); pool.setMaxWaitMillis(5000); pool.setBlockWhenExhausted(true); } /** * Serialize an object to JSON string. * * @param obj * @return */ public static String toJsonString(Object obj) { return toJsonString(obj, null); } /** * Serialize an object to JSON string, with a custom class loader. * * @param obj * @param classLoader * @return */ public static String toJsonString(Object obj, ClassLoader classLoader) { if (obj == null) { return "null"; } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.writeValueAsString(obj); } finally { poolMapper.returnObject(mapper); } } throw new SerializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Serialize an object to {@link JsonNode}. * * @param obj * @return * @since 0.6.2 */ public static JsonNode toJson(Object obj) { return toJson(obj, null); } /** * Serialize an object to {@link JsonNode}, with a custom class loader. * * @param obj * @param classLoader * @return * @since 0.6.2 */ public static JsonNode toJson(Object obj, ClassLoader classLoader) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { if (obj == null) { return NullNode.instance; } ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.valueToTree(obj); } finally { poolMapper.returnObject(mapper); } } throw new SerializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Read a JSON string and parse to {@link JsonNode} instance. * * @param source * @return * @since 0.6.2 */ public static JsonNode readJson(String source) { return readJson(source, null); } /** * Read a JSON string and parse to {@link JsonNode} instance, with a custom class loader. * * @param source * @param classLoader * @return * @since 0.6.2 */ public static JsonNode readJson(String source, ClassLoader classLoader) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { if (StringUtils.isBlank(source)) { return NullNode.instance; } ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.readTree(source); } finally { poolMapper.returnObject(mapper); } } throw new SerializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Read a JSON string and parse to {@link JsonNode} instance. * * @param source * @return * @since 0.6.2 */ public static JsonNode readJson(byte[] source) { return readJson(source, null); } /** * Read a JSON string and parse to {@link JsonNode} instance, with a custom class loader. * * @param source * @param classLoader * @return * @since 0.6.2 */ public static JsonNode readJson(byte[] source, ClassLoader classLoader) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { if (source == null || source.length == 0) { return NullNode.instance; } ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.readTree(source); } finally { poolMapper.returnObject(mapper); } } throw new SerializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Read a JSON string and parse to {@link JsonNode} instance. * * @param source * @return * @since 0.6.2 */ public static JsonNode readJson(InputStream source) { return readJson(source, null); } /** * Read a JSON string and parse to {@link JsonNode} instance, with a custom class loader. * * @param source * @param classLoader * @return * @since 0.6.2 */ public static JsonNode readJson(InputStream source, ClassLoader classLoader) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { if (source == null) { return NullNode.instance; } ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.readTree(source); } finally { poolMapper.returnObject(mapper); } } throw new SerializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Read a JSON string and parse to {@link JsonNode} instance. * * @param source * @return * @since 0.6.2 */ public static JsonNode readJson(Reader source) { return readJson(source, null); } /** * Read a JSON string and parse to {@link JsonNode} instance, with a custom class loader. * * @param source * @param classLoader * @return * @since 0.6.2 */ public static JsonNode readJson(Reader source, ClassLoader classLoader) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { if (source == null) { return NullNode.instance; } ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.readTree(source); } finally { poolMapper.returnObject(mapper); } } throw new SerializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Deserialize a JSON string. * * @param jsonString * @return */ public static Object fromJsonString(String jsonString) { return fromJsonString(jsonString, Object.class, null); } /** * Deserialize a JSON string, with custom class loader. * * @param jsonString * @param classLoader * @return */ public static Object fromJsonString(String jsonString, ClassLoader classLoader) { return fromJsonString(jsonString, Object.class, classLoader); } /** * Deserialize a JSON string. * * @param jsonString * @param clazz * @return */ public static T fromJsonString(String jsonString, Class clazz) { return fromJsonString(jsonString, clazz, null); } /** * Deserialize a JSON string, with custom class loader. * * @param jsonString * @param clazz * @return */ public static T fromJsonString(String jsonString, Class clazz, ClassLoader classLoader) { if (jsonString == null) { return null; } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.readValue(jsonString, clazz); } finally { poolMapper.returnObject(mapper); } } throw new DeserializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof DeserializationException ? (DeserializationException) e : new DeserializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Deserialize a {@link JsonNode}. * * @param json * @return * @since 0.6.2 */ public static Object fromJson(JsonNode json) { return fromJson(json, Object.class, null); } /** * Deserialize a {@link JsonNode}, with custom class loader. * * @param json * @param classLoader * @return * @since 0.6.2 */ public static Object fromJson(JsonNode json, ClassLoader classLoader) { return fromJson(json, Object.class, classLoader); } /** * Deserialize a {@link JsonNode}. * * @param json * @param clazz * @return * @since 0.6.2 */ public static T fromJson(JsonNode json, Class clazz) { return fromJson(json, clazz, null); } /** * Deserialize a {@link JsonNode}, with custom class loader. * * @param json * @param clazz * @return * @since 0.6.2 */ public static T fromJson(JsonNode json, Class clazz, ClassLoader classLoader) { if (json == null) { return null; } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { ObjectMapper mapper = poolMapper.borrowObject(); if (mapper != null) { try { return mapper.readValue(json.toString(), clazz); } finally { poolMapper.returnObject(mapper); } } throw new DeserializationException("No ObjectMapper instance avaialble!"); } catch (Exception e) { throw e instanceof DeserializationException ? (DeserializationException) e : new DeserializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /*----------------------------------------------------------------------*/ private static ThreadLocal fstConf = new ThreadLocal() { public FSTConfiguration initialValue() { FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration(); conf.setForceSerializable(true); return conf; } }; /** * Serialize an object to byte array. * *

* This method uses FST lib. *

* * @param obj * @return * @since 0.6.0 */ public static byte[] toByteArrayFst(Object obj) { return toByteArrayFst(obj, null); } /** * Serialize an object to byte array, with a custom class loader. * *

* This method uses FST lib. *

* * @param obj * @param classLoader * @return * @since 0.6.0 */ public static byte[] toByteArrayFst(final Object obj, final ClassLoader classLoader) { if (obj == null) { return null; } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { FSTConfiguration conf = fstConf.get(); conf.setClassLoader(classLoader != null ? classLoader : oldClassLoader); return conf.asByteArray(obj); } catch (Exception e) { throw e instanceof SerializationException ? (SerializationException) e : new SerializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Deserialize a byte array back to an object. * *

* This method uses FST lib. *

* * @param data * @return * @since 0.6.0 */ public static Object fromByteArrayFst(byte[] data) { return fromByteArrayFst(data, Object.class, null); } /** * Deserialize a byte array back to an object, with custom class loader. * *

* This method uses FST lib. *

* * @param data * @param classLoader * @return * @since 0.6.0 */ public static Object fromByteArrayFst(byte[] data, ClassLoader classLoader) { return fromByteArrayFst(data, Object.class, classLoader); } /** * Deserialize a byte array back to an object. * *

* This method uses FST lib. *

* * @param data * @param clazz * @return * @since 0.6.0 */ public static T fromByteArrayFst(byte[] data, Class clazz) { return fromByteArrayFst(data, clazz, null); } /** * Deserialize a byte array back to an object, with custom class loader. * *

* This method uses FST lib. *

* * @param data * @param clazz * @param classLoader * @return * @since 0.6.0 */ @SuppressWarnings("unchecked") public static T fromByteArrayFst(final byte[] data, final Class clazz, final ClassLoader classLoader) { if (data == null) { return null; } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(classLoader); } try { FSTConfiguration conf = fstConf.get(); conf.setClassLoader(classLoader != null ? classLoader : oldClassLoader); Object result = conf.asObject(data); if (result != null && clazz.isAssignableFrom(result.getClass())) { return (T) result; } else { return null; } } catch (Exception e) { throw e instanceof DeserializationException ? (DeserializationException) e : new DeserializationException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy