package serializers; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.OutputStream; import java.lang.reflect.Array; import java.util.ArrayList; import org.jboss.marshalling.ByteInput; import org.jboss.marshalling.ByteOutput; import org.jboss.marshalling.ClassExternalizerFactory; import org.jboss.marshalling.ClassTable; import org.jboss.marshalling.Creator; import org.jboss.marshalling.Externalizer; import org.jboss.marshalling.Marshaller; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; import org.jboss.marshalling.MarshallingConfiguration; import org.jboss.marshalling.Unmarshaller; import org.jboss.marshalling.reflect.SunReflectiveCreator; import org.jboss.marshalling.river.RiverMarshallerFactory; import org.jboss.marshalling.serial.SerialMarshallerFactory; import data.media.Image; import data.media.Media; import data.media.MediaContent; public class JBossMarshalling { public static void register(final TestGroups groups) { MarshallerFactory riverFactory = new RiverMarshallerFactory(); groups.media.add( JavaBuiltIn.mediaTransformer, new MarshallingSerializer<MediaContent>( MediaContent.class, "jboss-marshalling-river", riverFactory, false, false ), new SerFeatures( SerFormat.BINARY, SerGraph.FULL_GRAPH, SerClass.ZERO_KNOWLEDGE, "full graph zero knowledge" ) ); groups.media.add( JavaBuiltIn.mediaTransformer, new MarshallingSerializer<MediaContent>( MediaContent.class, "jboss-marshalling-river-manual", riverFactory, false, true ), new SerFeatures( SerFormat.BINARY, SerGraph.FULL_GRAPH, SerClass.MANUAL_OPT, "full graph with manual optimizations" ) ); groups.media.add( JavaBuiltIn.mediaTransformer, new MarshallingSerializer<MediaContent>( MediaContent.class, "jboss-marshalling-river-ct", riverFactory, true, false ), new SerFeatures( SerFormat.BINARY, SerGraph.FULL_GRAPH, SerClass.CLASSES_KNOWN, "full graph with preregistered classes" ) ); groups.media.add( JavaBuiltIn.mediaTransformer, new MarshallingSerializer<MediaContent>( MediaContent.class, "jboss-marshalling-river-ct-manual", riverFactory, true, true ), new SerFeatures( SerFormat.BINARY, SerGraph.FULL_GRAPH, SerClass.MANUAL_OPT, "full graph preregistered classes, manual optimization" ) ); groups.media.add( JavaBuiltIn.mediaTransformer, new MarshallingSerializer<MediaContent>( MediaContent.class, "jboss-marshalling-serial", new SerialMarshallerFactory(), false, false ), new SerFeatures( SerFormat.BINARY, SerGraph.FULL_GRAPH, SerClass.ZERO_KNOWLEDGE, "" ) ); } private static final class MarshallingSerializer<T> extends Serializer<T> { private final Class<T> clz; private final Marshaller marshaller; private final Unmarshaller unmarshaller; private final String name; private final ByteArrayInput input = new ByteArrayInput(); private final ByteArrayOutput output = new ByteArrayOutput(); public MarshallingSerializer( final Class<T> clz, final String name, final MarshallerFactory marshallerFactory, final boolean useCustomClassTable, final boolean useExternalizers ) { this.clz = clz; this.name = name; MarshallingConfiguration cfg = new MarshallingConfiguration(); cfg.setBufferSize(Serializer.BUFFER_SIZE); cfg.setExternalizerCreator(new SunReflectiveCreator()); if (useCustomClassTable) { cfg.setClassTable(new CustomClassTable()); } if (useExternalizers) { cfg.setClassExternalizerFactory(new CustomCEF()); } try { marshaller = marshallerFactory.createMarshaller(cfg); unmarshaller = marshallerFactory.createUnmarshaller(cfg); } catch (IOException e) { throw new RuntimeException(e); } } @Override public String getName() { return name; } @Override public T deserialize(final byte[] array) throws Exception { input.setBuffer(array); unmarshaller.start(input); T val = unmarshaller.readObject(clz); unmarshaller.finish(); return val; } @Override public byte[] serialize(final T data) throws IOException { marshaller.start(output); marshaller.writeObject(data); marshaller.finish(); return output.toByteArray(); } @Override public final void serializeItems(final T[] items, final OutputStream os) throws Exception { marshaller.start(Marshalling.createByteOutput(os)); for (Object item : items) { marshaller.writeObject(item); } marshaller.finish(); } @Override public T[] deserializeItems(final InputStream in, final int numOfItems) throws Exception { unmarshaller.start(Marshalling.createByteInput(in)); @SuppressWarnings("unchecked") T[] result = (T[]) Array.newInstance(clz, numOfItems); for (int i = 0; i < numOfItems; ++i) { result[i] = unmarshaller.readObject(clz); } unmarshaller.finish(); return result; } private static void writeMaybeString( final ObjectOutput output, final String s ) throws IOException { if (s != null) { output.writeBoolean(true); output.writeUTF(s); } else { output.writeBoolean(false); } } private static String readMaybeString(final ObjectInput input) throws IOException { if (input.readBoolean()) { return input.readUTF(); } else { return null; } } private static Image readImage(final ObjectInput input) throws IOException { final Image image = new Image(); readImage(input, image); return image; } private static void readImage(final ObjectInput input, final Image img) throws IOException { img.setUri(input.readUTF()); img.setTitle(readMaybeString(input)); img.setWidth(input.readInt()); img.setHeight(input.readInt()); img.setSize(Image.Size.values()[input.readByte()]); } private static void writeImage( final ObjectOutput output, final Image image ) throws IOException { output.writeUTF(image.uri); writeMaybeString(output, image.title); output.writeInt(image.width); output.writeInt(image.height); output.writeByte(image.size.ordinal()); } private static Media readMedia(final ObjectInput input) throws IOException { final Media m = new Media(); readMedia(input, m); return m; } private static void readMedia(final ObjectInput input, final Media m) throws IOException { m.setUri(input.readUTF()); m.setTitle(readMaybeString(input)); m.setWidth(input.readInt()); m.setHeight(input.readInt()); m.setFormat(input.readUTF()); m.setDuration(input.readLong()); m.setSize(input.readLong()); if (input.readBoolean()) { m.setBitrate(input.readInt()); } int numPersons = input.readInt(); ArrayList<String> persons = new ArrayList<String>(numPersons); for (int i = 0; i < numPersons; i++) { persons.add(input.readUTF()); } m.setPersons(persons); m.setPlayer(Media.Player.values()[input.readByte()]); m.setCopyright(readMaybeString(input)); } private static void writeMedia( final ObjectOutput output, final Media m ) throws IOException { output.writeUTF(m.uri); writeMaybeString(output, m.title); output.writeInt(m.width); output.writeInt(m.height); output.writeUTF(m.format); output.writeLong(m.duration); output.writeLong(m.size); output.writeBoolean(m.hasBitrate); if (m.hasBitrate) { output.writeInt(m.bitrate); } output.writeInt(m.persons.size()); for (String p : m.persons) { output.writeUTF(p); } output.writeByte(m.player.ordinal()); writeMaybeString(output, m.copyright); } private static void writeMediaContent( final ObjectOutput output, final MediaContent mc ) throws IOException { writeMedia(output, mc.media); output.writeInt(mc.images.size()); for (Image image : mc.images) { writeImage(output, image); } } private static void readMediaContent( final ObjectInput input, final MediaContent mc ) throws IOException { mc.setMedia(readMedia(input)); int numImages = input.readInt(); ArrayList<Image> images = new ArrayList<Image>(numImages); for (int i = 0; i < numImages; i++) { images.add(readImage(input)); } mc.setImages(images); } private static final class CustomCEF implements ClassExternalizerFactory { private static Class<?>[] CLASSES = { Media.class, MediaContent.class, Image.class, MediaExternalizer.class, MediaContentExternalizer.class, ImageExternalizer.class }; private static final Externalizer[] EXTERNALIZERS = { new MediaExternalizer(), new MediaContentExternalizer(), new ImageExternalizer(), null, null, null }; public CustomCEF() { ExternalizerExternalizer ext = new ExternalizerExternalizer(); EXTERNALIZERS[3] = ext; EXTERNALIZERS[4] = ext; EXTERNALIZERS[5] = ext; } @Override public Externalizer getExternalizer(final Class<?> type) { for (int i = 0; i < CLASSES.length; i++) { if (CLASSES[i].equals(type)) { return EXTERNALIZERS[i]; } } if (!ExternalizerExternalizer.class.equals(type)) { System.err.println("No externalizer for type " + type); } return null; } } private static final class MediaExternalizer implements Externalizer { private static final long serialVersionUID = 1L; @Override public void writeExternal( final Object subject, final ObjectOutput output ) throws IOException { writeMedia(output, (Media)subject); } @Override public void readExternal( final Object subject, final ObjectInput input ) throws IOException, ClassNotFoundException { readMedia(input, (Media)subject); } @Override public Object createExternal( final Class<?> subjectType, final ObjectInput input, final Creator defaultCreator ) throws IOException, ClassNotFoundException { return new Media(); } } private static final class MediaContentExternalizer implements Externalizer { private static final long serialVersionUID = 1L; @Override public void writeExternal( final Object subject, final ObjectOutput output ) throws IOException { writeMediaContent(output, (MediaContent)subject); } @Override public void readExternal( final Object subject, final ObjectInput input ) throws IOException, ClassNotFoundException { readMediaContent(input, (MediaContent)subject); } @Override public Object createExternal( final Class<?> subjectType, final ObjectInput input, final Creator defaultCreator ) throws IOException, ClassNotFoundException { return new MediaContent(); } } private static final class ExternalizerExternalizer implements Externalizer { private static final long serialVersionUID = 1L; private static final Externalizer[] EXTERNALIZERS = { new MediaExternalizer(), new MediaContentExternalizer(), new ImageExternalizer() }; @Override public void writeExternal( final Object subject, final ObjectOutput output ) throws IOException { // there is no state } @Override public void readExternal( final Object subject, final ObjectInput input ) throws IOException, ClassNotFoundException { // there is no state } @Override public Object createExternal( final Class<?> subjectType, final ObjectInput input, final Creator defaultCreator ) throws IOException, ClassNotFoundException { for (Externalizer ext : EXTERNALIZERS) { if (ext.getClass().equals(subjectType)) { return ext; } } throw new ClassNotFoundException("Unknown type " + subjectType); } } private static final class ImageExternalizer implements Externalizer { private static final long serialVersionUID = 1L; @Override public void writeExternal( final Object subject, final ObjectOutput output ) throws IOException { writeImage(output, (Image)subject); } @Override public void readExternal( final Object subject, final ObjectInput input ) throws IOException, ClassNotFoundException { readImage(input, (Image)subject); } @Override public Object createExternal( final Class<?> subjectType, final ObjectInput input, final Creator defaultCreator ) throws IOException, ClassNotFoundException { return new Image(); } } private static final class CustomClassTable implements ClassTable { private static final Class<?>[] CLASSES = { MediaContent.class, Media.Player.class, Media.class, Image.Size.class, Image.class, CustomCEF.class, MediaExternalizer.class, MediaContentExternalizer.class, ImageExternalizer.class, ExternalizerExternalizer.class, ArrayList.class }; private static final Writer WRITERS[] = new Writer[CLASSES.length]; public CustomClassTable() { for (int i = 0; i < WRITERS.length; i++) { final byte b = (byte)i; WRITERS[i] = new Writer() { @Override public void writeClass( final Marshaller marshaller, final Class<?> clazz ) throws IOException { marshaller.writeByte(b); } }; } } @Override public Writer getClassWriter(final Class<?> c) throws IOException { for (int i = 0; i < CLASSES.length; i++) { if (CLASSES[i].equals(c)) { return WRITERS[i]; } } throw new IOException("Unexpected class " + c); } @Override public Class<?> readClass(final Unmarshaller unmarshaller) throws IOException, ClassNotFoundException { byte b = unmarshaller.readByte(); if (b < 0 || b >= CLASSES.length) { throw new ClassNotFoundException( "Unexcepted class number " + b ); } return CLASSES[b]; } } private static final class ByteArrayInput implements ByteInput { private byte[] buffer; private int position; public void setBuffer(final byte[] buffer) { this.buffer = buffer; position = 0; } @Override public void close() throws IOException { buffer = null; position = -1; } @Override public int read() throws IOException { if (position >= buffer.length) { return -1; } return buffer[position++]; } @Override public int read(final byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(final byte[] b, final int off, final int len) throws IOException { if (position >= buffer.length) { return -1; } int n = len; if (n > buffer.length - position) { n = buffer.length - position; } System.arraycopy(buffer, position, b, off, n); position += n; return n; } @Override public int available() throws IOException { return buffer.length - position; } @Override public long skip(final long n) throws IOException { throw new IOException("Unsupported operation"); } } private static final class ByteArrayOutput implements ByteOutput { private byte[] buffer = new byte[Serializer.BUFFER_SIZE]; private int position; @Override public void close() throws IOException { position = 0; buffer = null; } @Override public void flush() throws IOException { } @Override public void write(final int b) throws IOException { throw new IOException("Unsupported operation"); } @Override public void write(final byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { if (buffer.length - position < len) { byte[] newBuffer = new byte[2 * buffer.length]; System.arraycopy(buffer, 0, newBuffer, 0, position); buffer = newBuffer; } System.arraycopy(b, off, buffer, position, len); position += len; } public byte[] toByteArray() { byte[] result = new byte[position]; System.arraycopy(buffer, 0, result, 0, position); position = 0; return result; } } } }