Package com.google.gwt.rpc.server

Source Code of com.google.gwt.rpc.server.CommandServerSerializationStreamWriter

/*
* 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.ArrayValueCommand;
import com.google.gwt.rpc.client.ast.CommandSink;
import com.google.gwt.rpc.client.ast.EnumValueCommand;
import com.google.gwt.rpc.client.ast.HasSetters;
import com.google.gwt.rpc.client.ast.IdentityValueCommand;
import com.google.gwt.rpc.client.ast.InstantiateCommand;
import com.google.gwt.rpc.client.ast.InvokeCustomFieldSerializerCommand;
import com.google.gwt.rpc.client.ast.NullValueCommand;
import com.google.gwt.rpc.client.ast.ValueCommand;
import com.google.gwt.rpc.client.impl.CommandSerializationStreamWriterBase;
import com.google.gwt.rpc.client.impl.HasValuesCommandSink;
import com.google.gwt.rpc.server.CommandSerializationUtil.Accessor;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;

import java.io.Serializable;
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.IdentityHashMap;
import java.util.Map;

/**
* A server-side implementation of SerializationStreamWriter that creates a
* command stream.
*/
public class CommandServerSerializationStreamWriter extends
    CommandSerializationStreamWriterBase {

  private final ClientOracle clientOracle;
  private final Map<Object, IdentityValueCommand> identityMap = new IdentityHashMap<Object, IdentityValueCommand>();

  public CommandServerSerializationStreamWriter(CommandSink sink) {
    this(new HostedModeClientOracle(), sink);
  }

  public CommandServerSerializationStreamWriter(ClientOracle oracle,
      CommandSink sink) {
    super(sink);
    this.clientOracle = oracle;
  }

  /**
   * Type is passed in to handle primitive types.
   */
  @Override
  protected ValueCommand makeValue(Class<?> type, Object value)
      throws SerializationException {
    if (value == null) {
      return NullValueCommand.INSTANCE;
    }

    Accessor accessor;

    if (identityMap.containsKey(value)) {
      return identityMap.get(value);

    } else if ((accessor = CommandSerializationUtil.getAccessor(type)).canMakeValueCommand()) {
      return accessor.makeValueCommand(value);

    } else if (type.isArray()) {
      return makeArray(type, value);

    } else if (Enum.class.isAssignableFrom(type)) {
      return makeEnum(value);

    } else {
      return makeObject(type, value);
    }
  }

  private ArrayValueCommand makeArray(Class<?> type, Object value)
      throws SerializationException {
    ArrayValueCommand toReturn = new ArrayValueCommand(type.getComponentType());
    for (int i = 0, j = Array.getLength(value); i < j; i++) {
      Object arrayValue = Array.get(value, i);
      if (arrayValue == null) {
        toReturn.add(NullValueCommand.INSTANCE);
      } else {
        Class<? extends Object> valueType = type.getComponentType().isPrimitive()
            ? type.getComponentType() : arrayValue.getClass();
        toReturn.add(makeValue(valueType, arrayValue));
      }
    }
    identityMap.put(value, toReturn);
    return toReturn;
  }

  private ValueCommand makeEnum(Object value) {
    EnumValueCommand toReturn = new EnumValueCommand();
    toReturn.setValue((Enum<?>) value);
    return toReturn;
  }

  /*
   * TODO: Profiling shows that the reflection and conditional logic in this
   * method is a hotspot. This could be remedied by generating synthetic
   * InstantiateCommand types that initialize themselves.
   */
  private IdentityValueCommand makeObject(Class<?> type, Object value)
      throws SerializationException {

    if (type.isAnonymousClass() || type.isLocalClass()) {
      throw new SerializationException(
          "Cannot serialize anonymous or local classes");
    }

    Class<?> manualType = type;
    Class<?> customSerializer;
    do {
      customSerializer = SerializabilityUtil.hasCustomFieldSerializer(manualType);
      if (customSerializer != null) {
        break;
      }
      manualType = manualType.getSuperclass();
    } while (manualType != null);

    IdentityValueCommand ins;
    if (customSerializer != null) {
      ins = serializeWithCustomSerializer(customSerializer, value, type,
          manualType);
    } else {
      ins = new InstantiateCommand(type);
      identityMap.put(value, ins);
    }

    /*
     * If we're looking at a subclass of a manually-serialized type, the
     * subclass must be tagged as serializable in order to qualify for
     * serialization.
     */
    if (type != manualType) {
      if (!Serializable.class.isAssignableFrom(type)
          && !IsSerializable.class.isAssignableFrom(type)) {
        throw new SerializationException(type.getName()
            + " is not a serializable type");
      }
    }

    while (type != manualType) {
      Field[] serializableFields = clientOracle.getOperableFields(type);
      for (Field declField : serializableFields) {
        assert (declField != null);

        Accessor accessor = CommandSerializationUtil.getAccessor(declField.getType());
        ValueCommand valueCommand;
        Object fieldValue = accessor.get(value, declField);
        if (fieldValue == null) {
          valueCommand = NullValueCommand.INSTANCE;
        } else {
          Class<? extends Object> fieldType = declField.getType().isPrimitive()
              ? declField.getType() : fieldValue.getClass();
          valueCommand = makeValue(fieldType, fieldValue);
        }

        ((HasSetters) ins).set(declField.getDeclaringClass(),
            declField.getName(), valueCommand);
      }
      type = type.getSuperclass();
    }
    return ins;
  }

  private InvokeCustomFieldSerializerCommand serializeWithCustomSerializer(
      Class<?> customSerializer, Object instance, Class<?> instanceClass,
      Class<?> manuallySerializedType) throws SerializationException {
    assert !instanceClass.isArray();

    Exception ex;
    try {
      /*
       * NB: Class.getMethod() wants exact formal types. It may be the case that
       * the custom serializer uses looser type bounds in its method
       * declarations.
       */
      for (Method method : customSerializer.getMethods()) {
        if ("serialize".equals(method.getName())) {
          assert Modifier.isStatic(method.getModifiers()) : "serialize method "
              + "in type " + customSerializer.getName() + " must be static";

          final InvokeCustomFieldSerializerCommand toReturn = new InvokeCustomFieldSerializerCommand(
              instanceClass, customSerializer, manuallySerializedType);
          identityMap.put(instance, toReturn);

          CommandServerSerializationStreamWriter subWriter = new CommandServerSerializationStreamWriter(
              clientOracle, new HasValuesCommandSink(toReturn));
          method.invoke(null, subWriter, instance);

          return toReturn;
        }
      }

      throw new NoSuchMethodException(
          "Could not find serialize method in custom serializer "
              + customSerializer.getName());

    } catch (SecurityException e) {
      ex = e;
    } catch (NoSuchMethodException e) {
      ex = e;
    } catch (IllegalArgumentException e) {
      ex = e;
    } catch (IllegalAccessException e) {
      ex = e;
    } catch (InvocationTargetException e) {
      ex = e;
    }

    throw new SerializationException(ex);
  }
}
TOP

Related Classes of com.google.gwt.rpc.server.CommandServerSerializationStreamWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.