blob: 510adc938192e23b7faf180c2544ec08c4a992b6 [file] [log] [blame]
/*
* Copyright 2006 Google Inc.
*
* 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 com.google.gwt.user.server.rpc.impl;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
/**
* For internal use only. Used for server call serialization. This class is
* carefully matched with the client-side version.
*/
public final class ServerSerializationStreamReader extends
AbstractSerializationStreamReader {
private ServerSerializableTypeOracle serializableTypeOracle;
private String[] stringTable;
private ArrayList tokenList = new ArrayList();
private int tokenListIndex;
public ServerSerializationStreamReader(
ServerSerializableTypeOracle serializableTypeOracle) {
this.serializableTypeOracle = serializableTypeOracle;
}
public Object deserializeValue(Class type) throws SerializationException {
if (type == boolean.class) {
return Boolean.valueOf(readBoolean());
} else if (type == byte.class) {
return new Byte(readByte());
} else if (type == char.class) {
return new Character(readChar());
} else if (type == double.class) {
return new Double(readDouble());
} else if (type == float.class) {
return new Float(readFloat());
} else if (type == int.class) {
return new Integer(readInt());
} else if (type == long.class) {
return new Long(readLong());
} else if (type == short.class) {
return new Short(readShort());
} else if (type == String.class) {
return readString();
}
return readObject();
}
public void prepareToRead(String encodedTokens) throws SerializationException {
tokenList.clear();
tokenListIndex = 0;
stringTable = null;
int idx = 0, nextIdx;
while (-1 != (nextIdx = encodedTokens.indexOf('\uffff', idx))) {
String current = encodedTokens.substring(idx, nextIdx);
tokenList.add(current);
idx = nextIdx + 1;
}
super.prepareToRead(encodedTokens);
// Read the type name table
//
deserializeStringTable();
}
public boolean readBoolean() {
return !extract().equals("0");
}
public byte readByte() {
return Byte.parseByte(extract());
}
public char readChar() {
// just use an int, it's more foolproof
return (char) Integer.parseInt(extract());
}
public double readDouble() {
return Double.parseDouble(extract());
}
public float readFloat() {
return Float.parseFloat(extract());
}
public int readInt() {
return Integer.parseInt(extract());
}
public long readLong() {
return Long.parseLong(extract());
}
public short readShort() {
return Short.parseShort(extract());
}
public String readString() throws SerializationException {
return getString(readInt());
}
protected Object deserialize(String typeSignature)
throws SerializationException {
Object instance = null;
SerializedInstanceReference serializedInstRef = serializableTypeOracle.decodeSerializedInstanceReference(typeSignature);
try {
Class instanceClass = Class.forName(serializedInstRef.getName(), false,
this.getClass().getClassLoader());
if (!serializableTypeOracle.isSerializable(instanceClass)) {
throw new SerializationException("Class '" + instanceClass.getName()
+ "' is not serializable");
}
validateTypeVersions(instanceClass, serializedInstRef);
Class customSerializer = serializableTypeOracle.hasCustomFieldSerializer(instanceClass);
instance = instantiate(customSerializer, instanceClass);
rememberDecodedObject(instance);
deserializeImpl(customSerializer, instanceClass, instance);
return instance;
} catch (ClassNotFoundException e) {
throw new SerializationException(e);
} catch (InstantiationException e) {
throw new SerializationException(e);
} catch (IllegalAccessException e) {
throw new SerializationException(e);
} catch (IllegalArgumentException e) {
throw new SerializationException(e);
} catch (InvocationTargetException e) {
throw new SerializationException(e);
} catch (NoSuchMethodException e) {
throw new SerializationException(e);
}
}
protected String getString(int index) {
if (index == 0) {
return null;
}
// index is 1-based
assert (index > 0);
assert (index <= stringTable.length);
return stringTable[index - 1];
}
private void deserializeImpl(Class customSerializer, Class instanceClass,
Object instance) throws NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException,
SerializationException, ClassNotFoundException {
if (customSerializer != null) {
deserializeWithCustomFieldDeserializer(customSerializer, instanceClass,
instance);
} else {
deserializeWithDefaultFieldDeserializer(instanceClass, instance);
}
}
private void deserializeStringTable() {
int typeNameCount = readInt();
stringTable = new String[typeNameCount];
for (int typeNameIndex = 0; typeNameIndex < typeNameCount; ++typeNameIndex) {
stringTable[typeNameIndex] = extract();
}
}
private void deserializeWithCustomFieldDeserializer(Class customSerializer,
Class instanceClass, Object instance) throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if (instanceClass.isArray()) {
Class componentType = instanceClass.getComponentType();
if (!componentType.isPrimitive()) {
instanceClass = Class.forName("[Ljava.lang.Object;");
}
}
Method deserialize = customSerializer.getMethod("deserialize", new Class[] {
SerializationStreamReader.class, instanceClass});
deserialize.invoke(null, new Object[] {this, instance});
}
private void deserializeWithDefaultFieldDeserializer(Class instanceClass,
Object instance) throws SerializationException, IllegalAccessException,
NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
Field[] declFields = instanceClass.getDeclaredFields();
Field[] serializableFields = serializableTypeOracle.applyFieldSerializationPolicy(declFields);
for (int index = 0; index < serializableFields.length; ++index) {
Field declField = serializableFields[index];
assert (declField != null);
Object value = deserializeValue(declField.getType());
boolean isAccessible = declField.isAccessible();
boolean needsAccessOverride = !isAccessible
&& !Modifier.isPublic(declField.getModifiers());
if (needsAccessOverride) {
// Override access restrictions
declField.setAccessible(true);
}
declField.set(instance, value);
if (needsAccessOverride) {
// Restore access restrictions
declField.setAccessible(isAccessible);
}
}
Class superClass = instanceClass.getSuperclass();
if (superClass != null && serializableTypeOracle.isSerializable(superClass)) {
deserializeImpl(
serializableTypeOracle.hasCustomFieldSerializer(superClass),
superClass, instance);
}
}
private String extract() {
return (String) tokenList.get(tokenListIndex++);
}
private Object instantiate(Class customSerializer, Class instanceClass)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
if (customSerializer != null) {
try {
Method instantiate = customSerializer.getMethod("instantiate",
new Class[] {SerializationStreamReader.class});
return instantiate.invoke(null, new Object[] {this});
} catch (NoSuchMethodException e) {
// purposely ignored
}
}
if (instanceClass.isArray()) {
int length = readInt();
Class componentType = instanceClass.getComponentType();
return Array.newInstance(componentType, length);
} else {
return instanceClass.newInstance();
}
}
private void validateTypeVersions(Class instanceClass,
SerializedInstanceReference serializedInstRef)
throws SerializationException {
String clientTypeSignature = serializedInstRef.getSignature();
if (clientTypeSignature.length() == 0) {
if (shouldEnforceTypeVersioning()) {
// TODO(mmendez): add a more descriptive error message here
throw new SerializationException();
}
return;
}
String serverTypeSignature = serializableTypeOracle.getSerializationSignature(instanceClass);
if (!clientTypeSignature.equals(serverTypeSignature)) {
throw new SerializationException("Invalid type signature for "
+ instanceClass.getName());
}
}
}