/* * Copyright 2008 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.IsSerializable; import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.server.rpc.SerializationPolicy; import java.io.Serializable; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * A serialization policy compatible with GWT 1.3.3 RPC. This is used when no * serialization policy file is present. * * <p> * The set of allowed types are: * </p> * <ol> * <li>Primitives</li> * <li>Types assignable to {@link IsSerializable}</li> * <li>Types with custom field serializers</li> * <li>Arrays of the above types</li> * </ol> * <p> * Types that derive from {@link Serializable} but do not meet any of the above * criteria may not be serialized as leaf types. However, their fields may be * serialized as super types of a legal type. * </p> */ public class LegacySerializationPolicy extends SerializationPolicy implements TypeNameObfuscator { private static final String ELISION_ERROR = "Type name elision in RPC " + "payloads is only supported if the RPC whitelist file is used."; /** * Many JRE types would appear to be {@link Serializable} on the server. * However, clients would not see these types as being {@link Serializable} * due to mismatches between the GWT JRE emulation and the real JRE. As a * workaround, this blacklist specifies a list of problematic types which * should be seen as not implementing {@link Serializable} for the purpose * matching the client's expectations. Note that a type on this list may still * be serializable via a custom serializer. */ private static final Class<?>[] JRE_BLACKLIST = { java.lang.ArrayStoreException.class, java.lang.AssertionError.class, java.lang.Boolean.class, java.lang.Byte.class, java.lang.Character.class, java.lang.Class.class, java.lang.ClassCastException.class, java.lang.Double.class, java.lang.Error.class, java.lang.Float.class, java.lang.IllegalArgumentException.class, java.lang.IllegalStateException.class, java.lang.IndexOutOfBoundsException.class, java.lang.Integer.class, java.lang.Long.class, java.lang.NegativeArraySizeException.class, java.lang.NullPointerException.class, java.lang.Number.class, java.lang.NumberFormatException.class, java.lang.Short.class, java.lang.StackTraceElement.class, java.lang.String.class, java.lang.StringBuffer.class, java.lang.StringIndexOutOfBoundsException.class, java.lang.UnsupportedOperationException.class, java.util.ArrayList.class, java.util.ConcurrentModificationException.class, java.util.Date.class, java.util.EmptyStackException.class, java.util.EventObject.class, java.util.HashMap.class, java.util.HashSet.class, java.util.MissingResourceException.class, java.util.NoSuchElementException.class, java.util.Stack.class, java.util.TooManyListenersException.class, java.util.Vector.class}; private static final Set<Class<?>> JRE_BLACKSET = new HashSet<Class<?>>( Arrays.asList(JRE_BLACKLIST)); private static final LegacySerializationPolicy sInstance = new LegacySerializationPolicy(); public static LegacySerializationPolicy getInstance() { return sInstance; } /** * Singleton. */ private LegacySerializationPolicy() { } /** * Implemented to fail with a useful error message. */ public final String getClassNameForTypeId(String id) throws SerializationException { throw new SerializationException(ELISION_ERROR); } /** * Implemented to fail with a useful error message. */ public final String getTypeIdForClass(Class<?> clazz) throws SerializationException { throw new SerializationException(ELISION_ERROR); } @Override public boolean shouldDeserializeFields(Class<?> clazz) { return isFieldSerializable(clazz); } @Override public boolean shouldSerializeFields(Class<?> clazz) { return isFieldSerializable(clazz); } @Override public void validateDeserialize(Class<?> clazz) throws SerializationException { if (!isInstantiable(clazz)) { throw new SerializationException("Type '" + clazz.getName() + "' was not assignable to '" + IsSerializable.class.getName() + "' and did not have a custom field serializer. " + "For security purposes, this type will not be deserialized."); } } @Override public void validateSerialize(Class<?> clazz) throws SerializationException { if (!isInstantiable(clazz)) { throw new SerializationException("Type '" + clazz.getName() + "' was not assignable to '" + IsSerializable.class.getName() + "' and did not have a custom field serializer." + "For security purposes, this type will not be serialized."); } } /** * Field serializable types are primitives, {@line IsSerializable}, * {@link Serializable}, types with custom serializers, and any arrays of * those types. */ private boolean isFieldSerializable(Class<?> clazz) { if (isInstantiable(clazz)) { return true; } if (Serializable.class.isAssignableFrom(clazz)) { return !JRE_BLACKSET.contains(clazz); } return false; } /** * Instantiable types are primitives, {@line IsSerializable}, types with * custom serializers, and any arrays of those types. Merely * {@link Serializable} types cannot be instantiated or serialized directly * (only as super types of legacy serializable types). */ private boolean isInstantiable(Class<?> clazz) { if (clazz.isPrimitive()) { return true; } if (clazz.isArray()) { return isInstantiable(clazz.getComponentType()); } if (IsSerializable.class.isAssignableFrom(clazz)) { return true; } return SerializabilityUtil.hasCustomFieldSerializer(clazz) != null; } }