/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2012.
*/
package x10.serialization;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map.Entry;
import x10.io.CustomSerialization;
import x10.rtt.RuntimeType;
import x10.runtime.impl.java.Runtime;
public final class X10JavaSerializer implements SerializationConstants {
protected final DataOutputStream out;
protected final ByteArrayOutputStream b_out;
// When a Object is serialized record its position
// N.B. use custom IdentityHashMap class, as standard one has poor performance on J9
X10IdentityHashMap<Object, Integer> objectMap = new X10IdentityHashMap<Object, Integer>();
int counter = 0;
// [GlobalGC] Table to remember serialized GlobalRefs, set and used in GlobalRef.java and InitDispatcher.java
X10IdentityHashMap<x10.core.GlobalRef<?>, Integer> grefMap = new X10IdentityHashMap<x10.core.GlobalRef<?>, Integer>(); // for GlobalGC
public void addToGrefMap(x10.core.GlobalRef<?> gr, int weight) { grefMap.put(gr, weight); }
public java.util.Map<x10.core.GlobalRef<?>, Integer> getGrefMap() { return grefMap; }
// Build up per-message id dictionary
protected SerializationDictionary idDictionary = new SerializationDictionary(FIRST_DYNAMIC_ID);
public X10JavaSerializer() {
this.b_out = new ByteArrayOutputStream();
this.out = new DataOutputStream(this.b_out);
}
public DataOutput getOutForHadoop() {
return out;
}
public int numBytesWritten() {
return b_out.size();
}
public byte[] toMessage() throws IOException {
out.close();
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Sending per-message serialization ids: "+idDictionary);
}
byte[] dictBytes = idDictionary.encode();
byte[] dataBytes = b_out.toByteArray();
byte[] message = new byte[dictBytes.length + dataBytes.length];
System.arraycopy(dictBytes, 0, message, 0, dictBytes.length);
System.arraycopy(dataBytes, 0, message, dictBytes.length, dataBytes.length);
return message;
}
public short getSerializationId(Class<?> clazz, Object obj) {
return idDictionary.getSerializationId(clazz, obj);
}
public void write(X10JavaSerializable obj) throws IOException {
if (obj == null) {
writeNull();
return;
}
short i = getSerializationId(obj.getClass(), obj);
if (i <= MAX_HARDCODED_ID) {
switch (i) {
case RTT_ANY_ID:
case RTT_BOOLEAN_ID:
case RTT_BYTE_ID:
case RTT_CHAR_ID:
case RTT_DOUBLE_ID:
case RTT_FLOAT_ID:
case RTT_INT_ID:
case RTT_LONG_ID:
case RTT_SHORT_ID:
case RTT_STRING_ID:
case RTT_UBYTE_ID:
case RTT_UINT_ID:
case RTT_ULONG_ID:
case RTT_USHORT_ID:
out.writeShort(i);
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Optimized serializing [**] a " + Runtime.ANSI_CYAN + "serialization_id_t" + Runtime.ANSI_RESET + ": " + i);
}
return;
default:
System.err.println("Unhanlded hardcoded serialization id");
throw new RuntimeException("Unhandled hard-wired serialization id in write(X10JavaSerializable)");
}
}
Integer pos = previous_position(obj, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [**] a " + Runtime.ANSI_CYAN + "serialization_id_t" + Runtime.ANSI_RESET + ": " + i);
}
out.writeShort(i);
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + Runtime.ANSI_BOLD + obj.getClass().getName() + Runtime.ANSI_RESET);
}
obj.$_serialize(this);
}
private void writeNull() throws IOException {
write(NULL_ID);
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a null reference");
}
}
public void write(CustomSerialization obj) throws IOException {
write((X10JavaSerializable) obj);
}
public void write(X10JavaSerializable obj[]) throws IOException {
write(obj.length);
for (X10JavaSerializable o : obj) {
write(o);
}
}
public void write(Object[] obj) throws IOException {
write(obj.length);
for (Object o : obj) {
write(o);
}
}
public void write(int i) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [****] an " + Runtime.ANSI_CYAN + "int" + Runtime.ANSI_RESET + ": " + i);
}
out.writeInt(i);
}
public void write(Integer p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing an " + Runtime.ANSI_CYAN + "Integer" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(INTEGER_ID);
out.writeInt(p.intValue());
}
public void write(int[] i) throws IOException {
out.writeInt(i.length);
for (int j : i) {
out.writeInt(j);
}
}
public void write(boolean b) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [*] a " + Runtime.ANSI_CYAN + "boolean" + Runtime.ANSI_RESET + ": " + b);
}
out.writeBoolean(b);
}
public void write(Boolean p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "Boolean" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(BOOLEAN_ID);
out.writeBoolean(p.booleanValue());
}
public void write(boolean[] v) throws IOException {
out.writeInt(v.length);
for (boolean b : v) {
out.writeBoolean(b);
}
}
public void write(char c) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [**] a " + Runtime.ANSI_CYAN + "char" + Runtime.ANSI_RESET + ": " + c);
}
out.writeChar(c);
}
public void write(Character p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "Character" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(CHARACTER_ID);
out.writeChar(p.charValue());
}
public void write(char[] v) throws IOException {
out.writeInt(v.length);
for (char c : v) {
out.writeChar(c);
}
}
public void write(byte b) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [*] a " + Runtime.ANSI_CYAN + "byte" + Runtime.ANSI_RESET + ": " + b);
}
out.writeByte(b);
}
public void write(Byte p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "Byte" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(BYTE_ID);
out.writeByte(p.byteValue());
}
public void write(byte[] b) throws IOException {
out.writeInt(b.length);
_write(b);
}
public void _write(byte[] b) throws IOException {
out.write(b);
}
public void write(short s) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [**] a " + Runtime.ANSI_CYAN + "short" + Runtime.ANSI_RESET + ": " + s);
}
out.writeShort(s);
}
public void write(Short p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "Short" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(SHORT_ID);
out.writeShort(p.shortValue());
}
public void write(short[] v) throws IOException {
out.writeInt(v.length);
for (short s : v) {
out.writeShort(s);
}
}
public void write(long l) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [********] a " + Runtime.ANSI_CYAN + "long" + Runtime.ANSI_RESET + ": " + l);
}
out.writeLong(l);
}
public void write(Long p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "Long" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(LONG_ID);
out.writeLong(p.longValue());
}
public void write(long[] v) throws IOException {
out.writeInt(v.length);
for (long l : v) {
out.writeLong(l);
}
}
public void write(double d) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [********] a " + Runtime.ANSI_CYAN + "double" + Runtime.ANSI_RESET + ": " + d);
}
out.writeDouble(d);
}
public void write(Double p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "Double" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(DOUBLE_ID);
out.writeDouble(p.doubleValue());
}
public void write(double[] v) throws IOException {
out.writeInt(v.length);
for (double d : v) {
out.writeDouble(d);
}
}
public void write(float f) throws IOException {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing [****] a " + Runtime.ANSI_CYAN + "float" + Runtime.ANSI_RESET + ": " + f);
}
out.writeFloat(f);
}
public void write(Float p) throws IOException {
if (p == null) {
writeNull();
return;
}
Integer pos = previous_position(p, true);
if (pos != null) {
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "Float" + Runtime.ANSI_RESET + ": " + p);
}
out.writeShort(FLOAT_ID);
out.writeFloat(p.floatValue());
}
public void write(float[] v) throws IOException {
out.writeInt(v.length);
for (float f : v) {
out.writeFloat(f);
}
}
public void write(Object v) throws IOException {
if (v == null) {
writeNull();
return;
}
if (v.getClass().isArray()) {
writeArrayUsingReflectionWithType(v);
return;
}
writeObjectUsingReflection(v);
}
public void write(String str) throws IOException {
if (str == null) {
writeNull();
return;
}
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("Serializing a " + Runtime.ANSI_CYAN + "String" + Runtime.ANSI_RESET + ": " + str);
}
Integer pos = previous_position(str, true);
if (pos != null) {
return;
}
write(STRING_ID);
writeStringValue(str);
}
void writeStringValue(String str) throws IOException {
write(str.length());
out.write(str.getBytes());
}
public void write(String[] v) throws IOException {
out.writeInt(v.length);
for (String str : v) {
write(str);
}
}
public void recordReference(Object obj) throws IOException {
previous_position(obj, false);
}
private Integer previous_position(Object obj, boolean writeRef) throws IOException {
Integer pos = objectMap.get(obj);
if (pos != null) {
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("\t\tFound repeated reference of type " + Runtime.ANSI_CYAN + Runtime.ANSI_BOLD + obj.getClass().getName() + Runtime.ANSI_RESET+ " at " + pos + " (absolute) in map");
}
// We have serialized this object before hence no need to do it again
if (writeRef) {
write(REPEATED_OBJECT_ID);
write(pos.intValue());
}
} else {
objectMap.put(obj, counter);
if (Runtime.TRACE_SER) {
Runtime.printTraceMessage("\t\tRecorded new reference of type " + Runtime.ANSI_CYAN + Runtime.ANSI_BOLD + obj.getClass().getName() + Runtime.ANSI_RESET + " at " + counter + " (absolute) in map");
}
counter++;
}
return pos;
}
public <T> void writeObjectUsingReflection(T body) throws IOException {
if (body == null) {
writeNull();
return;
}
Integer pos = previous_position(body, true);
if (pos != null) {
return;
}
// Special case: optimize transmission of RTT's for primitives
if (body instanceof x10.rtt.RuntimeType<?>) {
int id = ((x10.rtt.RuntimeType<?>) body).$_get_serialization_id();
if (id <= MAX_HARDCODED_ID) {
write(id);
return;
}
}
try {
Class<? extends Object> bodyClass = body.getClass();
write(getSerializationId(bodyClass, body));
SerializerThunk st = SerializerThunk.getSerializerThunk(bodyClass);
st.serializeObject(body, bodyClass, this);
} catch (SecurityException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
// This method is called from generated code when an X10 class has a Java superclass
public <T> void serializeClassUsingReflection(T obj, Class<T> clazz) throws IOException {
try {
SerializerThunk st = SerializerThunk.getSerializerThunk(clazz);
st.serializeObject(obj, clazz, this);
} catch (SecurityException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
static final boolean THROWABLES_SERIALIZE_MESSAGE = true;
static final boolean THROWABLES_SERIALIZE_STACKTRACE = true;
static final boolean THROWABLES_SERIALIZE_CAUSE = true;
public void writeArrayUsingReflection(Object obj) throws IOException {
if (obj == null) {
writeNull();
return;
}
Integer pos = previous_position(obj, true);
if (pos != null) {
return;
}
write(JAVA_ARRAY_ID);
int length = Array.getLength(obj);
write(length);
Class<?> componentType = obj.getClass().getComponentType();
if (componentType.isPrimitive()) {
if ("int".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getInt(obj, i));
}
} else if ("double".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getDouble(obj, i));
}
} else if ("float".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getFloat(obj, i));
}
} else if ("boolean".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getBoolean(obj, i));
}
} else if ("byte".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getByte(obj, i));
}
} else if ("short".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getShort(obj, i));
}
} else if ("long".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getLong(obj, i));
}
} else if ("char".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
write(Array.getChar(obj, i));
}
}
} else if ("java.lang.String".equals(componentType.getName())) {
for (int i = 0; i < length; i++) {
String str = (String) Array.get(obj, i);
write(str);
}
} else if (componentType.isArray()) {
for (int i = 0; i < length; i++) {
Object o = Array.get(obj, i);
writeArrayUsingReflection(o);
}
} else {
for (int i = 0; i < length; i++) {
Object o = Array.get(obj, i);
writeObjectUsingReflection(o);
}
}
}
public void writeArrayUsingReflectionWithType(Object obj) throws IOException {
if (obj == null) {
writeNull();
return;
}
Integer pos = previous_position(obj, true);
if (pos != null) {
return;
}
write(JAVA_ARRAY_ID);
Class<?> componentType = obj.getClass().getComponentType();
if (componentType.isPrimitive()) {
if ("int".equals(componentType.getName())) {
write(INTEGER_ID);
write((int[]) obj);
} else if ("double".equals(componentType.getName())) {
write(DOUBLE_ID);
write((double[]) obj);
} else if ("float".equals(componentType.getName())) {
write(FLOAT_ID);
write((float[]) obj);
} else if ("boolean".equals(componentType.getName())) {
write(BOOLEAN_ID);
write((boolean[]) obj);
} else if ("byte".equals(componentType.getName())) {
write(BYTE_ID);
write((byte[]) obj);
} else if ("short".equals(componentType.getName())) {
write(SHORT_ID);
write((short[]) obj);
} else if ("long".equals(componentType.getName())) {
write(LONG_ID);
write((long[]) obj);
} else if ("char".equals(componentType.getName())) {
write(CHARACTER_ID);
write((char[]) obj);
}
} else if ("java.lang.String".equals(componentType.getName())) {
write(STRING_ID);
write((java.lang.String[]) obj);
} else {
write(getSerializationId(componentType, null));
if (componentType.isArray()) {
int length = Array.getLength(obj);
write(length);
for (int i = 0; i < length; ++i) {
Object o = Array.get(obj, i);
writeArrayUsingReflection(o);
}
} else {
write((java.lang.Object[]) obj);
}
}
}
<T> void writeStringUsingReflection(Field field, T obj) throws IllegalAccessException, IOException {
String str = (String) field.get(obj);
write(str);
}
<T> void writePrimitiveUsingReflection(Field field, T obj) throws IllegalAccessException, IOException {
Class<?> type = field.getType();
if ("int".equals(type.getName())) {
write(field.getInt(obj));
} else if ("double".equals(type.getName())) {
write(field.getDouble(obj));
} else if ("float".equals(type.getName())) {
write(field.getFloat(obj));
} else if ("boolean".equals(type.getName())) {
write(field.getBoolean(obj));
} else if ("byte".equals(type.getName())) {
write(field.getByte(obj));
} else if ("short".equals(type.getName())) {
write(field.getShort(obj));
} else if ("long".equals(type.getName())) {
write(field.getLong(obj));
} else if ("char".equals(type.getName())) {
write(field.getChar(obj));
}
}
// Write an object using java serialization.
// This is used by IMC to optimize the serialization of primitive arrays
public void writeUsingObjectOutputStream(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(this.out);
oos.writeObject(obj);
}
}