/*
* Copyright 2016 higherfrequencytrading.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.lang.io.serialization;
import net.openhft.lang.io.AbstractBytes;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.NativeBytes;
import net.openhft.lang.io.serialization.impl.NoMarshaller;
import net.openhft.lang.io.serialization.impl.StringBuilderPool;
import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
import net.openhft.lang.model.constraints.NotNull;
import net.openhft.lang.pool.EnumInterner;
import java.io.Externalizable;
import java.io.IOException;
/**
* An extension of built-in Java serialization, featuring special treatment of {@link
* BytesMarshallable} objects, compact {@link String} encoding and support of pluggable custom
* serializers for arbitrary classes.
*
* <p>{@code BytesMarshallableSerializer} could benefit if objects (either top-level serialized or
* nested fields) implement {@link BytesMarshallable} interface the same way as built-in
* serialization benefit if objects implement {@link Externalizable} (of cause, {@code
* BytesMarshallableSerializer} supports {@code Externalizable} too).
*
* <p>{@link CharSequence}s, including {@code String}s (either top-level serialized or nested
* fields) are serialized in UTF-8 encoding.
*
* <p>Custom per-class serializers are held by {@link BytesMarshallerFactory}, which could be
* passed via constructor or static factory {@link #create create()} method.
*
* @see #create(BytesMarshallerFactory, ObjectSerializer)
*/
public class BytesMarshallableSerializer implements ObjectSerializer {
private static final long serialVersionUID = 0L;
private static final byte NULL = 'N';
private static final byte ENUMED = 'E';
private static final byte SERIALIZED = 'S';
private static final StringBuilderPool SBP = new StringBuilderPool();
private final BytesMarshallerFactory bytesMarshallerFactory;
private final ObjectSerializer objectSerializer;
protected BytesMarshallableSerializer(BytesMarshallerFactory bytesMarshallerFactory, ObjectSerializer objectSerializer) {
this.bytesMarshallerFactory = bytesMarshallerFactory;
this.objectSerializer = objectSerializer;
}
static boolean autoGenerateMarshaller(Object obj) {
return (obj instanceof Comparable && obj.getClass().getPackage().getName().startsWith("java"))
|| obj instanceof Externalizable
|| obj instanceof BytesMarshallable;
}
public static ObjectSerializer create() {
return create(new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE);
}
public static ObjectSerializer create(BytesMarshallerFactory bytesMarshallerFactory, ObjectSerializer instance) {
return bytesMarshallerFactory == null ? instance : new BytesMarshallableSerializer(bytesMarshallerFactory, instance);
}
@Override
public void writeSerializable(Bytes bytes, Object object, Class expectedClass) throws IOException {
if (object == null) {
bytes.writeByte(NULL);
return;
}
if (expectedClass != null) {
if (BytesMarshallable.class.isAssignableFrom(expectedClass)) {
((BytesMarshallable) object).writeMarshallable(bytes);
return;
} else if (Externalizable.class.isAssignableFrom(expectedClass)) {
((Externalizable) object).writeExternal(bytes);
return;
} else if (CharSequence.class.isAssignableFrom(expectedClass)) {
bytes.writeUTFΔ((CharSequence) object);
return;
} else if (Enum.class.isAssignableFrom(expectedClass)) {
bytes.write8bitText(object.toString());
return;
}
}
writeSerializable2(bytes, object);
}
private void writeSerializable2(Bytes bytes, Object object) throws IOException {
Class<?> clazz = object.getClass();
BytesMarshaller em = bytesMarshallerFactory.acquireMarshaller(clazz, false);
if (em == NoMarshaller.INSTANCE && autoGenerateMarshaller(object))
em = bytesMarshallerFactory.acquireMarshaller(clazz, true);
if (em != NoMarshaller.INSTANCE) {
if (em instanceof CompactBytesMarshaller) {
bytes.writeByte(((CompactBytesMarshaller) em).code());
em.write(bytes, object);
return;
}
bytes.writeByte(ENUMED);
this.writeSerializable(bytes, clazz, Class.class);
em.write(bytes, object);
return;
}
bytes.writeByte(SERIALIZED);
// TODO this is the lame implementation, but it works.
objectSerializer.writeSerializable(bytes, object, null);
}
@Override
public <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException {
if (expectedClass != null) {
try {
if (BytesMarshallable.class.isAssignableFrom(expectedClass)) {
return readBytesMarshallable(bytes, expectedClass, object);
} else if (Externalizable.class.isAssignableFrom(expectedClass)) {
return readExternalizable(bytes, expectedClass, object);
} else if (CharSequence.class.isAssignableFrom(expectedClass)) {
return readCharSequence(bytes, object);
} else if (Enum.class.isAssignableFrom(expectedClass)) {
StringBuilder sb = SBP.acquireStringBuilder();
bytes.read8bitText(sb);
return (T) EnumInterner.intern((Class<Enum>) expectedClass, sb);
}
} catch (InstantiationException e) {
throw new IOException("Unable to create " + expectedClass, e);
}
}
int type = bytes.readUnsignedByteOrThrow();
switch (type) {
case AbstractBytes.END_OF_BUFFER:
case NULL:
return null;
case ENUMED: {
Class clazz = this.readSerializable(bytes, Class.class, null);
assert clazz != null;
return (T) bytesMarshallerFactory.acquireMarshaller(clazz, true).read(bytes, object);
}
case SERIALIZED: {
return objectSerializer.readSerializable(bytes, expectedClass, object);
}
default:
BytesMarshaller<Object> m = bytesMarshallerFactory.getMarshaller((byte) type);
if (m == null)
throw new IllegalStateException("Unknown type " + (char) type);
return (T) m.read(bytes);
}
}
private <T> T readCharSequence(Bytes bytes, T object) {
if (object instanceof StringBuilder) {
bytes.readUTFΔ(((StringBuilder) object));
return object;
} else {
return (T) bytes.readUTFΔ();
}
}
private <T> T readExternalizable(Bytes bytes, Class<T> expectedClass, T object) throws InstantiationException, IOException, ClassNotFoundException {
if (object == null)
object = (T) NativeBytes.UNSAFE.allocateInstance(expectedClass);
((Externalizable) object).readExternal(bytes);
return object;
}
private <T> T readBytesMarshallable(Bytes bytes, Class<T> expectedClass, T object) throws InstantiationException {
if (object == null)
object = (T) NativeBytes.UNSAFE.allocateInstance(expectedClass);
((BytesMarshallable) object).readMarshallable(bytes);
return object;
}
}