blob: 4ebfbfc68b900018f1bab7beceda378ab0a9076b [file] [log] [blame]
/*
* 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.core.shared.GWT;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.GwtTransient;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.CRC32;
/**
* Serialization utility class used by the server-side RPC code.
*/
public class SerializabilityUtil {
/**
* Comparator used to sort fields.
*/
public static final Comparator<Field> FIELD_COMPARATOR = new Comparator<Field>() {
@Override
public int compare(Field f1, Field f2) {
return f1.getName().compareTo(f2.getName());
}
};
/**
* A permanent cache of all computed CRCs on classes. This is safe to do
* because a Class is guaranteed not to change within the lifetime of a
* ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, String> classCRC32Cache =
new ConcurrentHashMap<Class<?>, String>();
/**
* A permanent cache of all serializable fields on classes. This is safe to do
* because a Class is guaranteed not to change within the lifetime of a
* ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, Field[]> classSerializableFieldsCache =
new ConcurrentHashMap<Class<?>, Field[]>();
/**
* A permanent cache of all which classes onto custom field serializers. This
* is safe to do because a Class is guaranteed not to change within the
* lifetime of a ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, Class<?>> classCustomSerializerCache =
new ConcurrentHashMap<Class<?>, Class<?>>();
/**
* A permanent cache of all which classes onto server-side custom field
* serializers. This is safe to do because a Class is guaranteed not to change
* within the lifetime of a ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, Class<?>> classServerCustomSerializerCache =
new ConcurrentHashMap<Class<?>, Class<?>>();
/**
* Map of {@link Class} objects to singleton instances of that
* {@link CustomFieldSerializer}.
*/
private static final Map<Class<?>, CustomFieldSerializer<?>> CLASS_TO_SERIALIZER_INSTANCE =
new ConcurrentHashMap<Class<?>, CustomFieldSerializer<?>>();
private static final String JRE_SERVER_SERIALIZER_PACKAGE = "com.google.gwt.user.server.rpc.core";
private static final String JRE_SERIALIZER_PACKAGE = "com.google.gwt.user.client.rpc.core";
/**
* A re-usable, non-functional {@link ServerCustomFieldSerializer} for when
* the Server Custom Field Serializer does not implement the
* {@link ServerCustomFieldSerializer} interface.
*/
private static final ServerCustomFieldSerializer<?> NO_SUCH_SERIALIZER =
new ServerCustomFieldSerializer<Object>() {
@Override
public void deserializeInstance(SerializationStreamReader streamReader, Object instance) {
throw new AssertionError("This should never be called.");
}
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
Object instance, Type[] expectedParameterTypes,
DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
throw new SerializationException("This should never be called.");
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, Object instance) {
throw new AssertionError("This should never be called.");
}
};
private static final Map<String, String> SERIALIZED_PRIMITIVE_TYPE_NAMES =
new HashMap<String, String>();
private static final Set<Class<?>> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES =
new HashSet<Class<?>>();
static {
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(boolean.class.getName(), "Z");
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(byte.class.getName(), "B");
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(char.class.getName(), "C");
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(double.class.getName(), "D");
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(float.class.getName(), "F");
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(int.class.getName(), "I");
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(long.class.getName(), "J");
SERIALIZED_PRIMITIVE_TYPE_NAMES.put(short.class.getName(), "S");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Boolean.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Byte.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Character.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Double.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Exception.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Float.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Integer.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Long.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Object.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Short.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(String.class);
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(Throwable.class);
try {
/*
* Work around for incompatible type hierarchy (and therefore signature)
* between JUnit3 and JUnit4. Do this via reflection so we don't force the
* server to depend on JUnit.
*/
Class<?> clazz = Class.forName("junit.framework.AssertionFailedError");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(clazz);
} catch (ClassNotFoundException dontCare) {
// Empty because we don't care
}
}
/**
* Returns the fields of a particular class that can be considered for
* serialization. The returned list will be sorted into a canonical order to
* ensure consistent answers.
*/
public static Field[] applyFieldSerializationPolicy(Class<?> clazz) {
Field[] serializableFields;
serializableFields = classSerializableFieldsCache.get(clazz);
if (serializableFields == null) {
ArrayList<Field> fieldList = new ArrayList<Field>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (fieldQualifiesForSerialization(field)) {
fieldList.add(field);
}
}
serializableFields = fieldList.toArray(new Field[fieldList.size()]);
// sort the fields by name
Arrays.sort(serializableFields, 0, serializableFields.length, FIELD_COMPARATOR);
classSerializableFieldsCache.put(clazz, serializableFields);
}
return serializableFields;
}
public static SerializedInstanceReference decodeSerializedInstanceReference(
String encodedSerializedInstanceReference) {
final String[] components =
encodedSerializedInstanceReference
.split(SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR);
return new SerializedInstanceReference() {
@Override
public String getName() {
return components.length > 0 ? components[0] : "";
}
@Override
public String getSignature() {
return components.length > 1 ? components[1] : "";
}
};
}
public static String encodeSerializedInstanceReference(Class<?> instanceType,
SerializationPolicy policy) {
return instanceType.getName() + SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR
+ getSerializationSignature(instanceType, policy);
}
/**
* Resolve type variables to concrete types if possible. Otherwise, just return
* the type variable.
*
* @param unresolved The type to resolve
* @param resolvedTypes A map of generic types to actual types.
* @return The actual type, which may be of any subclass of Type.
*/
public static Type findActualType(Type unresolved,
DequeMap<TypeVariable<?>, Type> resolvedTypes) {
// Handle simple cases quickly.
if (!(unresolved instanceof TypeVariable<?>)) {
return unresolved;
}
TypeVariable<?> var = (TypeVariable<?>) unresolved;
Type target = resolvedTypes.get(var);
if (target == null || target == var) {
return var;
}
if (!(target instanceof TypeVariable<?>)) {
return target;
}
// Type variables that point to other type variables might form a cycle, which
// means they're all equivalent. Keep track of visited type variables to detect this.
Set<TypeVariable<?>> seen = new HashSet<TypeVariable<?>>();
seen.add(var);
var = (TypeVariable<?>) target;
seen.add(var);
while (true) {
target = resolvedTypes.get(var);
if (target == null || target == var) {
return var;
}
if (!(target instanceof TypeVariable<?>)) {
return target;
}
var = (TypeVariable<?>) target;
if (!seen.add(var)) {
// Cycle detected; returning an arbitrary var in the cycle.
return var;
}
}
}
/**
* Determine the expected types for any instance type parameters.
*
* This method also determines whether or not the instance can be assigned to
* the expected type. We combine the tasks because they require traversing the
* same data structures.
*
* @param instanceClass The instance for which we want generic parameter types
* @param expectedType The type we are expecting this instance to be
* @param resolvedTypes The types that have been resolved to actual values
* @return The expected types of the instance class' parameters. If null, the
* instance class is not assignable to the expected type.
*/
public static Type[] findExpectedParameterTypes(Class<?> instanceClass,
Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes) {
// Make a copy of the instance parameters from its class
TypeVariable<?>[] instanceTypes = instanceClass.getTypeParameters();
Type[] expectedParameterTypes = Arrays.copyOf(instanceTypes, instanceTypes.length,
Type[].class);
// Determine the type we are really expecting, to the best of our knowledge
if (expectedType == null) {
// Match up parameters assuming that the instance class is the best match
// for the expected class, which we can only do if we have resolved types.
if (resolvedTypes != null) {
findInstanceParameters(instanceClass, resolvedTypes, expectedParameterTypes);
}
// With no expected type, the instance is assignable and we fall through
} else {
// First determine what type we are really expecting. The type may still
// be a TypeVariable<?> at this time when we are deserializing class
// fields or components of another data structure.
Type actualType = findActualType(expectedType, resolvedTypes);
// Try to match the instanceClass to the expected type, updating the
// expectedParameterTypes so that we can track them back to the resolved
// types once we determine what class to treat the instance as. In
// this method, only type information from the instance is used to resolve
// types because the information in the resolved types map is only
// relevant to the expected type(s). We still pass in the resolvedTypes
// because we may need to resolve expected types.
// Note that certain expected types may require the instance to extend
// or implement multiple classes or interfaces, so we may capture multiple
// types here.
Set<Class<?>> expectedInstanceClasses = new HashSet<Class<?>>();
if (!findExpectedInstanceClass(instanceClass, actualType,
resolvedTypes, expectedInstanceClasses, expectedParameterTypes)) {
// If we could not match the instance to the expected, it is not assignable
// and we are done.
return null;
}
// Now that we know what class the instance should be,
// get any remaining parameters using resolved types.
if (resolvedTypes != null) {
for (Class<?> expectedClass : expectedInstanceClasses) {
findInstanceParameters(expectedClass, resolvedTypes, expectedParameterTypes);
}
}
}
return expectedParameterTypes;
}
/**
* Find the Class that a given type refers to.
*
* @param type The type of interest
* @return The Class that type represents
*/
public static Class<?> getClassFromType(Type type,
DequeMap<TypeVariable<?>, Type> resolvedTypes) {
Type actualType = findActualType(type, resolvedTypes);
if (actualType instanceof Class) {
return (Class<?>) actualType;
}
if (type instanceof ParameterizedType) {
return getClassFromType(((ParameterizedType) actualType).getRawType(), resolvedTypes);
}
return null;
}
public static String getSerializationSignature(Class<?> instanceType,
SerializationPolicy policy) {
String result;
result = classCRC32Cache.get(instanceType);
if (result == null) {
CRC32 crc = new CRC32();
try {
generateSerializationSignature(instanceType, crc, policy);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Could not compute the serialization signature", e);
}
result = Long.toString(crc.getValue());
classCRC32Cache.put(instanceType, result);
}
return result;
}
public static String getSerializedTypeName(Class<?> instanceType) {
if (instanceType.isPrimitive()) {
return SERIALIZED_PRIMITIVE_TYPE_NAMES.get(instanceType.getName());
}
return instanceType.getName();
}
/**
* Returns the {@link Class} which can serialize the given instance type, or
* <code>null</code> if this class has no custom field serializer.
*
* Note that arrays never have custom field serializers.
*/
public static Class<?> hasCustomFieldSerializer(Class<?> instanceType) {
assert (instanceType != null);
if (instanceType.isArray()) {
return null;
}
Class<?> result;
result = classCustomSerializerCache.get(instanceType);
if (result == null) {
result = computeHasCustomFieldSerializer(instanceType, false);
if (result == null) {
/*
* Use (result == instanceType) as a sentinel value when the class has
* no custom field serializer. We avoid using null as the sentinel
* value, because that would necessitate an additional containsKey()
* check in the most common case.
*/
result = instanceType;
}
classCustomSerializerCache.put(instanceType, result);
}
return (result == instanceType) ? null : result;
}
/**
* Returns the server-side {@link Class} which can serialize the given
* instance type, or <code>null</code> if this class has no type-checking
* custom field serializer.
*
* Note that arrays never have custom field serializers.
*/
public static Class<?> hasServerCustomFieldSerializer(Class<?> instanceType) {
assert (instanceType != null);
if (instanceType.isArray()) {
return null;
}
Class<?> result;
result = classServerCustomSerializerCache.get(instanceType);
if (result == null) {
result = computeHasCustomFieldSerializer(instanceType, true);
if (result == null) {
/*
* Use (result == instanceType) as a sentinel value when the class has
* no custom field serializer. We avoid using null as the sentinel
* value, because that would necessitate an additional containsKey()
* check in the most common case.
*/
result = instanceType;
}
classServerCustomSerializerCache.put(instanceType, result);
}
return (result == instanceType) ? null : result;
}
/**
* Remove all of the actual types that arose from the given type.
*
* This method should always be called after a corresponding call to
* resolveTypes.
*
* @param methodType The type we wish to assign this instance to
* @param resolvedTypes The types that have been resolved to actual values
*/
public static void releaseTypes(Type methodType, DequeMap<TypeVariable<?>, Type> resolvedTypes) {
SerializabilityUtil.resolveTypesWorker(methodType, resolvedTypes, false);
}
/**
* Find all the actual types we can from the information in the given type,
* and put the mapping from TypeVariable objects to actual types into the
* resolved types map.
*
* The method releaseTypes should always be called after a call to this
* method, unless the resolved types map is about to be discarded.
*
* @param methodType The type we wish to assign this instance to
* @param resolvedTypes The types that have been resolved to actual values
*/
public static void resolveTypes(Type methodType, DequeMap<TypeVariable<?>, Type> resolvedTypes) {
SerializabilityUtil.resolveTypesWorker(methodType, resolvedTypes, true);
}
/**
* Returns true if this field has an annotation named "GwtTransient".
*/
static boolean hasGwtTransientAnnotation(Field field) {
for (Annotation a : field.getAnnotations()) {
if (a.annotationType().getSimpleName().equals(GwtTransient.class.getSimpleName())) {
return true;
}
}
return false;
}
static boolean isNotStaticTransientOrFinal(Field field) {
/*
* Only serialize fields that are not static, transient (including
* @GwtTransient), or final.
*/
int fieldModifiers = field.getModifiers();
return !Modifier.isStatic(fieldModifiers) && !Modifier.isTransient(fieldModifiers)
&& !hasGwtTransientAnnotation(field) && !Modifier.isFinal(fieldModifiers);
}
/**
* Loads a {@link CustomFieldSerializer} from a class that may implement that
* interface.
*
* @param customSerializerClass the Custom Field Serializer class
*
* @return an instance the class provided if it implements
* {@link CustomFieldSerializer} or {@code null} if it does not
*
* @throws SerializationException if the load process encounters an unexpected
* problem
*/
static CustomFieldSerializer<?> loadCustomFieldSerializer(final Class<?> customSerializerClass)
throws SerializationException {
CustomFieldSerializer<?> customFieldSerializer =
CLASS_TO_SERIALIZER_INSTANCE.get(customSerializerClass);
if (customFieldSerializer == null) {
if (CustomFieldSerializer.class.isAssignableFrom(customSerializerClass)) {
try {
customFieldSerializer = (CustomFieldSerializer<?>) customSerializerClass.newInstance();
} catch (InstantiationException e) {
throw new SerializationException(e);
} catch (IllegalAccessException e) {
throw new SerializationException(e);
}
} else {
customFieldSerializer = NO_SUCH_SERIALIZER;
}
CLASS_TO_SERIALIZER_INSTANCE.put(customSerializerClass, customFieldSerializer);
}
if (customFieldSerializer == NO_SUCH_SERIALIZER) {
return null;
} else {
return customFieldSerializer;
}
}
/**
* This method treats arrays in a special way.
*/
private static Class<?> computeHasCustomFieldSerializer(Class<?> instanceType,
Boolean typeChecked) {
assert (instanceType != null);
String qualifiedTypeName = instanceType.getName();
/*
* This class is called from client code running in Development Mode as well
* as server code running in the servlet container. In Development Mode, we
* want to load classes through the
* CompilingClassLoader$MultiParentClassLoader, not the system classloader.
*/
ClassLoader classLoader =
GWT.isClient() ? SerializabilityUtil.class.getClassLoader() : Thread.currentThread()
.getContextClassLoader();
if (typeChecked) {
/*
* Look for a server-specific version of the custom field serializer.
* Server-side versions do additional type checking before deserializing a
* class, offering protection against certain malicious attacks on the
* server via RPC.
*/
String serverSerializerName = qualifiedTypeName + "_ServerCustomFieldSerializer";
serverSerializerName = serverSerializerName.replaceFirst("client", "server");
Class<?> serverCustomSerializer = getCustomFieldSerializer(classLoader, serverSerializerName);
if (serverCustomSerializer != null) {
return serverCustomSerializer;
}
// Try with the regular name
serverCustomSerializer =
getCustomFieldSerializer(classLoader, JRE_SERVER_SERIALIZER_PACKAGE + "."
+ serverSerializerName);
if (serverCustomSerializer != null) {
return serverCustomSerializer;
}
}
// Look for client-side serializers.
String simpleSerializerName = qualifiedTypeName + "_CustomFieldSerializer";
Class<?> customSerializer = getCustomFieldSerializer(classLoader, simpleSerializerName);
if (customSerializer != null) {
return customSerializer;
}
// Try with the regular name
customSerializer =
getCustomFieldSerializer(classLoader, JRE_SERIALIZER_PACKAGE + "." + simpleSerializerName);
if (customSerializer != null) {
return customSerializer;
}
return null;
}
private static boolean excludeImplementationFromSerializationSignature(Class<?> instanceType) {
if (TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.contains(instanceType)) {
return true;
}
return false;
}
private static boolean fieldQualifiesForSerialization(Field field) {
if (Throwable.class == field.getDeclaringClass()) {
/**
* Only serialize Throwable's detailMessage field; all others are ignored.
*
* NOTE: Changing the set of fields that we serialize for Throwable will
* necessitate a change to our JRE emulation's version of Throwable.
*/
if ("detailMessage".equals(field.getName())) {
assert (isNotStaticTransientOrFinal(field));
return true;
} else {
return false;
}
} else {
return isNotStaticTransientOrFinal(field);
}
}
private static boolean findExpectedInstanceClass(Class<?> instanceClass,
Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes,
Set<Class<?>> expectedInstanceClasses, Type[] expectedParameterTypes) {
// Check for an exact match for the instance class and the expected type.
// If found, we can return. For expected types that allow one of several
// possible class to match (wildcards, type variables) see if this
// instance matches any of the allowed types.
if (expectedType instanceof TypeVariable) {
// Every bound must have a match
Type[] typeVariableBounds = ((TypeVariable<?>) expectedType).getBounds();
for (Type boundType : typeVariableBounds) {
if (!findExpectedInstanceClass(instanceClass, boundType, resolvedTypes,
expectedInstanceClasses, expectedParameterTypes)) {
return false;
}
}
return true;
} else if (expectedType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) expectedType;
if (paramType.getRawType() == instanceClass) {
expectedInstanceClasses.add(instanceClass);
return true;
}
} else if (expectedType instanceof GenericArrayType) {
if (instanceClass.isArray()) {
expectedInstanceClasses.add(instanceClass);
return true;
}
} else if (expectedType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) expectedType;
Type[] lowerBounds = wildcardType.getLowerBounds();
for (Type type : lowerBounds) {
/* Require instance to be a superclass of type, or type itself. */
Class<?> boundClass = getClassFromType(type, resolvedTypes);
while (boundClass != null) {
if (instanceClass == boundClass) {
expectedInstanceClasses.add(boundClass);
break;
}
boundClass = boundClass.getSuperclass();
}
// We fail if the class does not meet any bound, as we should.
if (boundClass == null) {
return false;
}
}
Type[] upperBounds = wildcardType.getUpperBounds();
for (Type type : upperBounds) {
/* Require instanceClass to be a subclass of type. */
if (!findExpectedInstanceClass(instanceClass, type, resolvedTypes,
expectedInstanceClasses, expectedParameterTypes)) {
return false;
}
}
return true;
} else if (((Class<?>) expectedType).getComponentType() != null) {
// Array types just pass through here, and we catch any problems when
// we try to deserialize the entries.
if (((Class<?>) expectedType).isAssignableFrom(instanceClass)) {
expectedInstanceClasses.add(instanceClass);
return true;
} else {
return false;
}
} else if (((Class<?>) expectedType) == instanceClass) {
expectedInstanceClasses.add(instanceClass);
return true;
}
// We know that the instance class does not exactly match the expected type,
// so try its superclass and its interfaces. At the same time, update any
// expected types to use the superclass or interface type. We know at this
// point that the expected type does not involve wildcards or TypeVariables,
// so we know that the first thing to return true indicates we are done,
// and failure of anything to return true means the instance does not meet
// the expected type.
if (instanceClass.getGenericSuperclass() != null) {
Type[] localTypes = expectedParameterTypes.clone();
if (findExpectedInstanceClassFromSuper(instanceClass.getGenericSuperclass(), expectedType,
resolvedTypes, expectedInstanceClasses, localTypes)) {
for (int i = 0; i < expectedParameterTypes.length; ++i) {
expectedParameterTypes[i] = localTypes[i];
}
return true;
}
}
Type[] interfaces = instanceClass.getGenericInterfaces();
for (Type interfaceType : interfaces) {
Type[] localTypes = expectedParameterTypes.clone();
if (findExpectedInstanceClassFromSuper(interfaceType, expectedType, resolvedTypes,
expectedInstanceClasses, localTypes)) {
for (int i = 0; i < expectedParameterTypes.length; ++i) {
expectedParameterTypes[i] = localTypes[i];
}
return true;
}
}
return false;
}
private static boolean findExpectedInstanceClassFromSuper(
Type superType, Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes,
Set<Class<?>> expectedInstanceClasses, Type[] expectedParameterTypes) {
if (superType instanceof GenericArrayType) {
// Can't use array types as supertypes or interfaces
return false;
} else if (superType instanceof Class) {
// No additional type info from the superclass
return findExpectedInstanceClass((Class<?>) superType, expectedType, resolvedTypes,
expectedInstanceClasses, expectedParameterTypes);
} else if (superType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) superType;
Type rawType = paramType.getRawType();
if (rawType instanceof Class) {
Class<?> rawClass = (Class<?>) rawType;
TypeVariable<?>[] classGenericTypes = rawClass.getTypeParameters();
Type[] actualTypes = paramType.getActualTypeArguments();
for (int i = 0; i < actualTypes.length; ++i) {
for (int j = 0; j < expectedParameterTypes.length; ++j) {
if (actualTypes[i] == expectedParameterTypes[j]) {
expectedParameterTypes[j] = classGenericTypes[i];
}
}
}
return findExpectedInstanceClass(rawClass, expectedType, resolvedTypes,
expectedInstanceClasses, expectedParameterTypes);
}
} else if (superType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) superType;
Type[] upperBounds = wildcardType.getUpperBounds();
for (Type boundType : upperBounds) {
if (findExpectedInstanceClassFromSuper(boundType, expectedType,
resolvedTypes, expectedInstanceClasses, expectedParameterTypes)) {
return true;
}
}
}
return false;
}
/**
* Attempt to find known type for TypeVariable type from an instance.
*
* @param foundParameter The currently known parameter, which must be of type
* TypeVariable
* @param instanceType The instance that we need to check for information
* about the type
* @param resolvedTypes The map of known relationships between Type objects
* @return A new value for the foundParameter, if we find one
*/
private static Type findInstanceParameter(Type foundParameter, Type instanceType,
DequeMap<TypeVariable<?>, Type> resolvedTypes) {
// See what we know about the types that are matched to this type.
if (instanceType instanceof GenericArrayType) {
return findInstanceParameter(foundParameter, ((GenericArrayType) instanceType)
.getGenericComponentType(), resolvedTypes);
} else if (instanceType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) instanceType;
Type rawType = paramType.getRawType();
if (rawType instanceof Class) {
Class<?> rawClass = (Class<?>) rawType;
TypeVariable<?>[] classGenericTypes = rawClass.getTypeParameters();
Type[] actualTypes = paramType.getActualTypeArguments();
for (int i = 0; i < actualTypes.length; ++i) {
if (actualTypes[i] == foundParameter) {
// Check if we already know about this type.
Type capturedType = findActualType(classGenericTypes[i], resolvedTypes);
if (capturedType != classGenericTypes[i]) {
return capturedType;
}
if (rawClass.getGenericSuperclass() != null) {
Type superParameter =
findInstanceParameter(classGenericTypes[i], rawClass.getGenericSuperclass(),
resolvedTypes);
if (!(superParameter instanceof TypeVariable)) {
return superParameter;
}
}
Type[] rawInterfaces = rawClass.getGenericInterfaces();
for (Type interfaceType : rawInterfaces) {
Type interfaceParameter =
findInstanceParameter(classGenericTypes[i], interfaceType, resolvedTypes);
if (!(interfaceParameter instanceof TypeVariable)) {
return interfaceParameter;
}
}
}
}
}
} else if (instanceType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) instanceType;
Type[] upperBounds = wildcardType.getUpperBounds();
for (Type boundType : upperBounds) {
Type wildcardParameter = findInstanceParameter(foundParameter, boundType, resolvedTypes);
if (!(wildcardParameter instanceof TypeVariable)) {
return wildcardParameter;
}
}
}
return foundParameter;
}
/**
* Attempt to find the actual types for the generic parameters of an instance,
* given the types we have resolved from the method signature or class field
* declaration.
*
* @param instanceClass The instance for which we want actual generic
* parameter types.
* @param resolvedTypes The types that have been resolved to actual values
* @param expectedParameterTypes An array of types representing the actual
* declared types for the parameters of the instanceClass. Some may
* be of type TypeVariable, in which case they need to be resolved, if
* possible, by this method.
*/
private static void findInstanceParameters(Class<?> instanceClass,
DequeMap<TypeVariable<?>, Type> resolvedTypes, Type[] expectedParameterTypes) {
TypeVariable<?>[] instanceTypes = instanceClass.getTypeParameters();
for (int i = 0; i < expectedParameterTypes.length; ++i) {
if (!(expectedParameterTypes[i] instanceof TypeVariable)) {
// We already have an actual type
continue;
}
// Check if we already know about this type.
boolean haveMatch = false;
for (int j = 0; !haveMatch && j < instanceTypes.length; ++j) {
if (expectedParameterTypes[i] == instanceTypes[j]) {
Type capturedType = findActualType(instanceTypes[j], resolvedTypes);
if (!(capturedType instanceof TypeVariable)) {
expectedParameterTypes[i] = capturedType;
haveMatch = true;
}
}
}
if (haveMatch) {
continue;
}
// Check if it is defined by superclasses
if (instanceClass.getGenericSuperclass() != null) {
Type superParameter = findInstanceParameter(expectedParameterTypes[i],
instanceClass.getGenericSuperclass(), resolvedTypes);
if (!(superParameter instanceof TypeVariable)) {
expectedParameterTypes[i] = superParameter;
continue;
}
}
// Check if it is defined by interfaces
Type[] interfaceTypes = instanceClass.getGenericInterfaces();
for (Type interfaceType : interfaceTypes) {
Type interfaceParameter =
findInstanceParameter(expectedParameterTypes[i], interfaceType, resolvedTypes);
if (!(interfaceParameter instanceof TypeVariable)) {
expectedParameterTypes[i] = interfaceParameter;
break;
}
}
}
}
private static void generateSerializationSignature(Class<?> instanceType, CRC32 crc,
SerializationPolicy policy) throws UnsupportedEncodingException {
crc.update(getSerializedTypeName(instanceType).getBytes(RPCServletUtils.CHARSET_UTF8));
if (excludeImplementationFromSerializationSignature(instanceType)) {
return;
}
Class<?> customSerializer = hasCustomFieldSerializer(instanceType);
if (customSerializer != null) {
generateSerializationSignature(customSerializer, crc, policy);
} else if (instanceType.isArray()) {
generateSerializationSignature(instanceType.getComponentType(), crc, policy);
} else if (Enum.class.isAssignableFrom(instanceType) && !Enum.class.equals(instanceType)) {
if (!instanceType.isEnum()) {
instanceType = instanceType.getSuperclass();
}
Enum<?>[] constants = instanceType.asSubclass(Enum.class).getEnumConstants();
for (Enum<?> constant : constants) {
crc.update(constant.name().getBytes(RPCServletUtils.CHARSET_UTF8));
}
} else if (!instanceType.isPrimitive()) {
Field[] fields = applyFieldSerializationPolicy(instanceType);
Set<String> clientFieldNames = policy.getClientFieldNamesForEnhancedClass(instanceType);
for (Field field : fields) {
assert (field != null);
/**
* If clientFieldNames is non-null, use only the fields listed there to
* generate the signature. Otherwise, use all known fields.
*/
if ((clientFieldNames == null) || clientFieldNames.contains(field.getName())) {
crc.update(field.getName().getBytes(RPCServletUtils.CHARSET_UTF8));
crc.update(getSerializedTypeName(field.getType()).getBytes(RPCServletUtils.CHARSET_UTF8));
}
}
Class<?> superClass = instanceType.getSuperclass();
if (superClass != null) {
generateSerializationSignature(superClass, crc, policy);
}
}
}
private static Class<?> getCustomFieldSerializer(ClassLoader classLoader,
String qualifiedSerialzierName) {
try {
Class<?> customSerializerClass = Class.forName(qualifiedSerialzierName, false, classLoader);
return customSerializerClass;
} catch (ClassNotFoundException e) {
return null;
}
}
private static void resolveTypesWorker(Type methodType,
DequeMap<TypeVariable<?>, Type> resolvedTypes, boolean addTypes) {
if (methodType instanceof GenericArrayType) {
SerializabilityUtil.resolveTypesWorker(((GenericArrayType) methodType)
.getGenericComponentType(), resolvedTypes, addTypes);
} else if (methodType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) methodType;
Type rawType = paramType.getRawType();
if (rawType instanceof Class) {
Class<?> rawClass = (Class<?>) paramType.getRawType();
TypeVariable<?>[] classGenericTypes = rawClass.getTypeParameters();
Type[] actualTypes = paramType.getActualTypeArguments();
for (int i = 0; i < actualTypes.length; ++i) {
TypeVariable<?> variableType = classGenericTypes[i];
if (addTypes) {
resolvedTypes.add(variableType, actualTypes[i]);
} else {
resolvedTypes.remove(variableType);
}
}
Class<?> superClass = rawClass.getSuperclass();
if (superClass != null) {
Type superGenericType = rawClass.getGenericSuperclass();
SerializabilityUtil.resolveTypesWorker(superGenericType, resolvedTypes, addTypes);
}
Type[] interfaceTypes = rawClass.getGenericInterfaces();
for (Type interfaceType : interfaceTypes) {
SerializabilityUtil.resolveTypesWorker(interfaceType, resolvedTypes, addTypes);
}
}
} else if (methodType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) methodType;
Type[] lowerBounds = wildcardType.getLowerBounds();
for (Type type : lowerBounds) {
SerializabilityUtil.resolveTypesWorker(type, resolvedTypes, addTypes);
}
Type[] upperBounds = wildcardType.getUpperBounds();
for (Type type : upperBounds) {
SerializabilityUtil.resolveTypesWorker(type, resolvedTypes, addTypes);
}
} else if (methodType instanceof TypeVariable) {
Type[] bounds = ((TypeVariable<?>) methodType).getBounds();
for (Type type : bounds) {
SerializabilityUtil.resolveTypesWorker(type, resolvedTypes, addTypes);
}
} else if (methodType instanceof Class) {
Class<?> classType = (Class<?>) methodType;
// A type that is of instance Class, with TypeParameters, must be a raw
// class, so strip off any parameters in the map.
TypeVariable<?>[] classParams = classType.getTypeParameters();
for (TypeVariable<?> classParamType : classParams) {
if (addTypes) {
resolvedTypes.add(classParamType, classParamType);
} else {
resolvedTypes.remove(classParamType);
}
}
Type superGenericType = classType.getGenericSuperclass();
if (superGenericType != null) {
SerializabilityUtil.resolveTypesWorker(superGenericType, resolvedTypes, addTypes);
}
Type[] interfaceTypes = classType.getGenericInterfaces();
for (Type interfaceType : interfaceTypes) {
SerializabilityUtil.resolveTypesWorker(interfaceType, resolvedTypes, addTypes);
}
}
}
}