package mil.nga.giat.geowave.core.index; import java.lang.reflect.Constructor; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A set of convenience methods for serializing and deserializing persistable * objects * */ public class PersistenceUtils { private final static Logger LOGGER = LoggerFactory.getLogger(PersistenceUtils.class); public static byte[] toBinary( final Collection<? extends Persistable> persistables ) { if (persistables.isEmpty()) { return new byte[] {}; } int byteCount = 4; final List<byte[]> persistableBinaries = new ArrayList<byte[]>(); for (final Persistable persistable : persistables) { final byte[] binary = toBinary(persistable); byteCount += (4 + binary.length); persistableBinaries.add(binary); } final ByteBuffer buf = ByteBuffer.allocate(byteCount); buf.putInt(persistables.size()); for (final byte[] binary : persistableBinaries) { buf.putInt(binary.length); buf.put(binary); } return buf.array(); } public static byte[] toBinary( final Persistable persistable ) { if (persistable == null) { return new byte[0]; } // preface the payload with the class name and a length of the class // name final byte[] className = StringUtils.stringToBinary(persistable.getClass().getName()); final byte[] persistableBinary = persistable.toBinary(); final int classNameLength = className.length; final ByteBuffer buf = ByteBuffer.allocate(4 + classNameLength + persistableBinary.length); buf.putInt(classNameLength); buf.put(className); buf.put(persistableBinary); return buf.array(); } public static List<Persistable> fromBinary( final byte[] bytes ) { final List<Persistable> persistables = new ArrayList<Persistable>(); if ((bytes == null) || (bytes.length < 4)) { // the original binary didn't even contain the size of the // array, assume that nothing was persisted return persistables; } final ByteBuffer buf = ByteBuffer.wrap(bytes); final int size = buf.getInt(); for (int i = 0; i < size; i++) { final byte[] persistableBinary = new byte[buf.getInt()]; buf.get(persistableBinary); persistables.add(fromBinary( persistableBinary, Persistable.class)); } return persistables; } public static <T extends Persistable> T fromBinary( final byte[] bytes, final Class<T> expectedType ) { final ByteBuffer buf = ByteBuffer.wrap(bytes); final int classNameLength = buf.getInt(); final byte[] classNameBinary = new byte[classNameLength]; final byte[] persistableBinary = new byte[bytes.length - classNameLength - 4]; buf.get(classNameBinary); final String className = StringUtils.stringFromBinary(classNameBinary); final T retVal = classFactory( className, expectedType); if (retVal != null) { buf.get(persistableBinary); retVal.fromBinary(persistableBinary); } return retVal; } @SuppressWarnings("unchecked") public static <T> T classFactory( final String className, final Class<T> expectedType ) { Class<?> factoryType = null; try { factoryType = Class.forName(className); } catch (final ClassNotFoundException e) { LOGGER.warn( "error creating class: could not find class ", e); } if (factoryType != null) { Object factoryClassInst = null; try { // use the no arg constructor and make sure its accessible // HP Fortify "Access Specifier Manipulation" // This method is being modified by trusted code, // in a way that is not influenced by user input final Constructor<?> noArgConstructor = factoryType.getDeclaredConstructor(); noArgConstructor.setAccessible(true); factoryClassInst = noArgConstructor.newInstance(); } catch (final Exception e) { LOGGER.warn( "error creating class: could not create class ", e); } if (factoryClassInst != null) { if (!expectedType.isAssignableFrom(factoryClassInst.getClass())) { LOGGER.warn("error creating class, does not implement expected type"); } else { return ((T) factoryClassInst); } } } return null; } }