/*
* Copyright 2008 Jeff Dwyer
*
* 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.apress.progwt.server.gwt;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.SerializationPolicy;
public class OneFourTenSerializationPolicy extends SerializationPolicy {
/**
* 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.Exception.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.RuntimeException.class, java.lang.Short.class,
java.lang.StackTraceElement.class, java.lang.String.class,
java.lang.StringBuffer.class,
java.lang.StringIndexOutOfBoundsException.class,
java.lang.Throwable.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 JRE_BLACKSET = new HashSet(Arrays
.asList(JRE_BLACKLIST));
private static final OneFourTenSerializationPolicy sInstance = new OneFourTenSerializationPolicy();
public static OneFourTenSerializationPolicy getInstance() {
return sInstance;
}
/**
* Singleton.
*/
private OneFourTenSerializationPolicy() {
}
/*
* (non-Javadoc)
*
* @see com.google.gwt.user.server.rpc.SerializationPolicy#shouldDerializeFields(java.lang.String)
*/
public boolean shouldDeserializeFields(Class clazz) {
return isFieldSerializable(clazz);
}
/*
* (non-Javadoc)
*
* @see com.google.gwt.user.server.rpc.SerializationPolicy#shouldSerializeFields(java.lang.String)
*/
public boolean shouldSerializeFields(Class clazz) {
return isFieldSerializable(clazz);
}
/*
* (non-Javadoc)
*
* @see com.google.gwt.user.server.rpc.SerializationPolicy#validateDeserialize(java.lang.String)
*/
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.");
}
}
/*
* (non-Javadoc)
*
* @see com.google.gwt.user.server.rpc.SerializationPolicy#validateSerialize(java.lang.String)
*/
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;
}
// NOTE this is the difference from Legacy
if (Serializable.class.isAssignableFrom(clazz)) {
return true;
}
return SerializabilityUtil_1_5_3.hasCustomFieldSerializer(clazz) != null;
}
}