| /* |
| * 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); |
| } |
| |
| @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() { |
| } |
| } |