blob: 5a88036cf50e300513d3b4fa1053316c26138a08 [file] [log] [blame]
/*
* Copyright 2009 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.rpc.server;
import com.google.gwt.rpc.client.ast.BooleanValueCommand;
import com.google.gwt.rpc.client.ast.ByteValueCommand;
import com.google.gwt.rpc.client.ast.CharValueCommand;
import com.google.gwt.rpc.client.ast.DoubleValueCommand;
import com.google.gwt.rpc.client.ast.FloatValueCommand;
import com.google.gwt.rpc.client.ast.IntValueCommand;
import com.google.gwt.rpc.client.ast.LongValueCommand;
import com.google.gwt.rpc.client.ast.ShortValueCommand;
import com.google.gwt.rpc.client.ast.StringValueCommand;
import com.google.gwt.rpc.client.ast.ValueCommand;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.IdentityHashMap;
import java.util.Map;
import sun.misc.Unsafe;
/**
* Contains common utility code.
*/
public class CommandSerializationUtil {
/**
* Defines methods for getting and setting fields.
*/
public interface Accessor {
boolean canMakeValueCommand();
/**
* Indicates if set can be called with a value of the given type.
*/
boolean canSet(Class<?> clazz);
Object get(Object instance, Field f);
Class<?> getTargetType();
ValueCommand makeValueCommand(Object value);
Object readNext(SerializationStreamReader reader)
throws SerializationException;
void set(Object instance, Field f, Object value);
void set(Object array, int index, Object value);
}
/**
* Defines type-specific methods of getting and setting fields.
*/
private static enum TypeAccessor implements Accessor {
BOOL {
@Override
public boolean canSet(Class<?> clazz) {
return Boolean.class.isAssignableFrom(clazz)
|| Number.class.isAssignableFrom(clazz)
|| String.class.isAssignableFrom(clazz);
}
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getBoolean(instance, offset);
}
@Override
public Object getDefaultValue() {
return false;
}
@Override
public Class<?> getTargetType() {
return boolean.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new BooleanValueCommand((Boolean) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readBoolean();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putBoolean(instance, offset, toBoolean(value));
}
private boolean toBoolean(Object value) {
if (value instanceof Number) {
return ((Number) value).intValue() != 0;
} else if (value instanceof String) {
return Boolean.valueOf((String) value);
} else {
// returns false if the value is null.
return Boolean.TRUE.equals(value);
}
}
},
BYTE {
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getByte(instance, offset);
}
@Override
public Class<?> getTargetType() {
return byte.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new ByteValueCommand((Byte) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readByte();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putByte(instance, offset, ((Number) value).byteValue());
}
},
CHAR {
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getChar(instance, offset);
}
@Override
public Class<?> getTargetType() {
return char.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new CharValueCommand((Character) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readChar();
}
@Override
public void set(Object instance, long offset, Object value) {
char c = (value instanceof Number) ? (char) ((Number) value).intValue()
: (Character) value;
theUnsafe.putChar(instance, offset, c);
}
},
DOUBLE {
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getDouble(instance, offset);
}
@Override
public Class<?> getTargetType() {
return double.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new DoubleValueCommand((Double) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readDouble();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putDouble(instance, offset, ((Number) value).doubleValue());
}
},
FLOAT {
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getFloat(instance, offset);
}
@Override
public Class<?> getTargetType() {
return float.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new FloatValueCommand((Float) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readFloat();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putFloat(instance, offset, ((Number) value).floatValue());
}
},
INT {
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getInt(instance, offset);
}
@Override
public Class<?> getTargetType() {
return int.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new IntValueCommand(((Number) value).intValue());
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readInt();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putInt(instance, offset, ((Number) value).intValue());
}
},
LONG {
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getLong(instance, offset);
}
@Override
public Class<?> getTargetType() {
return long.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new LongValueCommand((Long) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readLong();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putLong(instance, offset, ((Number) value).longValue());
}
},
OBJECT {
@Override
public boolean canMakeValueCommand() {
return false;
}
@Override
public boolean canSet(Class<?> clazz) {
return Object.class.isAssignableFrom(clazz);
}
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getObject(instance, offset);
}
@Override
public Object getDefaultValue() {
return null;
}
@Override
public Class<?> getTargetType() {
return Object.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
throw new RuntimeException("Cannot call makeValueCommand for Objects");
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readObject();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putObject(instance, offset, value);
}
},
SHORT {
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getShort(instance, offset);
}
@Override
public Class<?> getTargetType() {
return short.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new ShortValueCommand((Short) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readShort();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putShort(instance, offset, ((Number) value).shortValue());
}
},
STRING {
@Override
public boolean canSet(Class<?> clazz) {
return true;
}
@Override
public Object get(Object instance, long offset) {
return theUnsafe.getObject(instance, offset);
}
@Override
public Object getDefaultValue() {
return null;
}
@Override
public Class<?> getTargetType() {
return String.class;
}
@Override
public ValueCommand makeValueCommand(Object value) {
return new StringValueCommand((String) value);
}
@Override
public Object readNext(SerializationStreamReader reader)
throws SerializationException {
return reader.readObject();
}
@Override
public void set(Object instance, long offset, Object value) {
theUnsafe.putObject(instance, offset, value == null ? null
: value.toString());
}
};
private final Class<?> arrayType = Array.newInstance(getTargetType(), 0).getClass();
private final long arrayBase = theUnsafe.arrayBaseOffset(arrayType);
private final long arrayIndexScale = theUnsafe.arrayIndexScale(arrayType);
public boolean canMakeValueCommand() {
return true;
}
public boolean canSet(Class<?> clazz) {
return Number.class.isAssignableFrom(clazz);
}
public Object get(Object instance, Field f) {
long offset = objectFieldOffset(f);
return get(instance, offset);
}
public abstract Object get(Object instance, long offset);
public Object getDefaultValue() {
return 0;
}
public abstract Class<?> getTargetType();
public abstract ValueCommand makeValueCommand(Object value);
public abstract Object readNext(SerializationStreamReader reader)
throws SerializationException;
public void set(Object instance, Field f, Object value) {
long offset = objectFieldOffset(f);
set(instance, offset, value == null ? getDefaultValue() : value);
}
public void set(Object array, int index, Object value) {
set(array, arrayBase + arrayIndexScale * index, value);
}
public abstract void set(Object instance, long offset, Object value);
}
private static final Map<Class<?>, Accessor> ACCESSORS = new IdentityHashMap<Class<?>, Accessor>();
private static final Unsafe theUnsafe;
static {
Exception ex = null;
Unsafe localUnsafe = null;
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
localUnsafe = (Unsafe) f.get(null);
} catch (SecurityException e) {
ex = e;
} catch (NoSuchFieldException e) {
ex = e;
} catch (IllegalArgumentException e) {
ex = e;
} catch (IllegalAccessException e) {
ex = e;
}
if (ex != null) {
throw new RuntimeException("Unable to get Unsafe instance", ex);
} else {
theUnsafe = localUnsafe;
}
for (TypeAccessor setter : TypeAccessor.values()) {
ACCESSORS.put(setter.getTargetType(), setter);
}
}
public static Accessor getAccessor(Class<?> clazz) {
Accessor toReturn = ACCESSORS.get(clazz);
if (toReturn == null) {
toReturn = TypeAccessor.OBJECT;
}
return toReturn;
}
/**
* TODO: In the future it may be preferable to use a custom ClassLoader to
* inject a constructor that will initialize all of the final fields that we
* care about.
*/
static <T> T allocateInstance(Class<T> clazz) throws InstantiationException {
Object obj = theUnsafe.allocateInstance(clazz);
return clazz.cast(obj);
}
private static long objectFieldOffset(Field f) {
return theUnsafe.objectFieldOffset(f);
}
private CommandSerializationUtil() {
}
}