/** * Copyright 2014-2018 yangming.liu<[email protected]>. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, see <http://www.gnu.org/licenses/>. */ package org.bytesoft.common.utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.objenesis.strategy.SerializingInstantiatorStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.caucho.hessian.io.HessianInput; import com.caucho.hessian.io.HessianOutput; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Kryo.DefaultInstantiatorStrategy; 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; public class SerializeUtils { static final Logger logger = LoggerFactory.getLogger(SerializeUtils.class); static final String SERIALIZER_NAME_DEFAULT = "default"; static final String SERIALIZER_NAME_KRYO = "kryo"; static final String SERIALIZER_NAME_HESSIAN = "hessian"; static final int SERIALIZER_DEFAULT = 0x0; static final int SERIALIZER_KRYO = 0x1; static final int SERIALIZER_HESSIAN = 0x2; static int PREFERRED_SERIALIZER = SERIALIZER_KRYO; static { String serializer = StringUtils.trimToNull(System.getProperty("bytejta.serializer.preferred")); if (StringUtils.isNotBlank(serializer) && StringUtils.equalsIgnoreCase(SERIALIZER_NAME_KRYO, serializer)) { PREFERRED_SERIALIZER = SERIALIZER_KRYO; } else if (StringUtils.isNotBlank(serializer) && StringUtils.equalsIgnoreCase(SERIALIZER_NAME_HESSIAN, serializer)) { PREFERRED_SERIALIZER = SERIALIZER_HESSIAN; } else if (StringUtils.isNotBlank(serializer)) { PREFERRED_SERIALIZER = SERIALIZER_DEFAULT; } } static KryoPool kryoPool = new KryoPool.Builder(new KryoFactory() { public Kryo create() { Kryo kryo = new Kryo(); kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new SerializingInstantiatorStrategy())); return kryo; } }).softReferences().build(); public static byte[] serializeObject(Serializable obj, int serializerType) throws IOException { int serializer = SERIALIZER_DEFAULT; byte[] dataArray = null; if (serializerType == SERIALIZER_KRYO) { dataArray = kryoSerialize(obj); serializer = SERIALIZER_KRYO; } else if (serializerType == SERIALIZER_HESSIAN) { dataArray = hessianSerialize(obj); serializer = SERIALIZER_HESSIAN; } else { dataArray = javaSerialize(obj); serializer = SERIALIZER_DEFAULT; } byte[] byteArray = new byte[dataArray.length + 1]; byteArray[0] = (byte) serializer; System.arraycopy(dataArray, 0, byteArray, 1, dataArray.length); return byteArray; } public static byte[] serializeObject(Serializable obj) throws IOException { if (PREFERRED_SERIALIZER == SERIALIZER_DEFAULT) { return serializeObject(obj, PREFERRED_SERIALIZER); } else { try { return serializeObject(obj, PREFERRED_SERIALIZER); } catch (Exception ex) { return serializeObject(obj, SERIALIZER_DEFAULT); } } } public static Serializable deserializeObject(byte[] bytes) throws IOException { if (bytes.length == 0) { throw new IllegalArgumentException(); } byte[] byteArray = new byte[bytes.length - 1]; int serializer = bytes[0]; if (serializer == SERIALIZER_KRYO) { System.arraycopy(bytes, 1, byteArray, 0, byteArray.length); return kryoDeserialize(byteArray); } else if (serializer == SERIALIZER_HESSIAN) { System.arraycopy(bytes, 1, byteArray, 0, byteArray.length); return hessianDeserialize(byteArray); } else if (serializer == SERIALIZER_DEFAULT) { System.arraycopy(bytes, 1, byteArray, 0, byteArray.length); return javaDeserialize(byteArray); } else { throw new IllegalArgumentException(); } } public static byte[] javaSerialize(final Serializable obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos)); try { oos.writeObject(obj); } finally { CommonUtils.closeQuietly(oos); } return baos.toByteArray(); } public static Serializable javaDeserialize(byte[] byteArray) throws IOException { ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(byteArray))); try { return (Serializable) ois.readObject(); } catch (ClassNotFoundException ex) { throw new IllegalStateException(ex); } finally { CommonUtils.closeQuietly(ois); } } public static byte[] kryoSerialize(final Serializable obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); final Output output = new Output(baos); try { kryoPool.run(new KryoCallback<Object>() { public Object execute(Kryo kryo) { kryo.writeClassAndObject(output, obj); return null; } }); } finally { CommonUtils.closeQuietly(output); } return baos.toByteArray(); } public static Serializable kryoDeserialize(byte[] byteArray) throws IOException { final Input input = new Input(new ByteArrayInputStream(byteArray)); try { return kryoPool.run(new KryoCallback<Serializable>() { public Serializable execute(Kryo kryo) { return (Serializable) kryo.readClassAndObject(input); } }); } finally { CommonUtils.closeQuietly(input); } } public static byte[] hessianSerialize(Serializable obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); HessianOutput ho = new HessianOutput(baos); try { ho.writeObject(obj); return baos.toByteArray(); } finally { CommonUtils.closeQuietly(baos); } } public static Serializable hessianDeserialize(byte[] bytes) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); HessianInput hi = new HessianInput(bais); try { Object result = hi.readObject(); return (Serializable) result; } finally { CommonUtils.closeQuietly(bais); } } public static String serializeClass(Class<?> clazz) { if (boolean.class.equals(clazz)) { return "Z"; } else if (byte.class.equals(clazz)) { return "B"; } else if (short.class.equals(clazz)) { return "S"; } else if (char.class.equals(clazz)) { return "C"; } else if (int.class.equals(clazz)) { return "I"; } else if (float.class.equals(clazz)) { return "F"; } else if (long.class.equals(clazz)) { return "J"; } else if (double.class.equals(clazz)) { return "D"; } else if (void.class.equals(clazz)) { return "V"; } else if (clazz.isArray()) { return clazz.getName(); } else { return String.format("L%s;", clazz.getName().replaceAll("\\.", "/")); } } public static Class<?> deserializeClass(String classDesc) { String clazz = StringUtils.trimToEmpty(classDesc); if (StringUtils.isBlank(clazz)) { throw new IllegalStateException(); } if (clazz.length() > 1) { String clazzName = clazz.replaceAll("\\/", "."); ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { return cl.loadClass(clazzName); } catch (ClassNotFoundException ex) { throw new IllegalStateException(ex.getMessage()); } } final char character = clazz.charAt(0); return SerializeUtils.deserializeClass(character); } public static Class<?> deserializeClass(final char character) { switch (character) { case 'Z': return boolean.class; case 'B': return byte.class; case 'S': return short.class; case 'C': return char.class; case 'I': return int.class; case 'J': return long.class; case 'F': return float.class; case 'D': return double.class; default: throw new IllegalStateException(); } } public static String serializeMethod(Method method) { StringBuilder ber = new StringBuilder(); ber.append(method.getName()).append("("); Class<?>[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { Class<?> parameterType = parameterTypes[i]; String clazzName = SerializeUtils.serializeClass(parameterType); ber.append(clazzName); } ber.append(")").append(SerializeUtils.serializeClass(method.getReturnType())); return ber.toString(); } public static Method deserializeMethod(Class<?> interfaceClass, String methodDesc) throws Exception { int startIdx = methodDesc.indexOf("("); String methodName = methodDesc.substring(0, startIdx); int endIndex = methodDesc.indexOf(")"); String value = methodDesc.substring(startIdx + 1, endIndex); char[] values = value.toCharArray(); List<Class<?>> paramTypeList = new ArrayList<Class<?>>(); boolean flags = false; StringBuilder clazzDesc = new StringBuilder(); for (int i = 0; i < values.length; i++) { char character = values[i]; if (character == ';') { flags = false; String paramTypeNameDesc = clazzDesc.toString(); clazzDesc.delete(0, clazzDesc.length()); Class<?> paramType = SerializeUtils.deserializeClass(paramTypeNameDesc); paramTypeList.add(paramType); continue; } else if (flags) { clazzDesc.append(character); continue; } else if (character == 'L') { flags = true; continue; } Class<?> paramType = SerializeUtils.deserializeClass(character); paramTypeList.add(paramType); } Class<?>[] parameterTypes = new Class<?>[paramTypeList.size()]; paramTypeList.toArray(parameterTypes); return interfaceClass.getDeclaredMethod(methodName, parameterTypes); } }