001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.user.server.rpc.impl;
017:
018: import java.io.UnsupportedEncodingException;
019: import java.lang.reflect.Field;
020: import java.lang.reflect.Modifier;
021: import java.util.ArrayList;
022: import java.util.Arrays;
023: import java.util.Comparator;
024: import java.util.HashMap;
025: import java.util.HashSet;
026: import java.util.IdentityHashMap;
027: import java.util.Map;
028: import java.util.Set;
029: import java.util.zip.CRC32;
030:
031: /**
032: * Serialization utility class used by the server-side RPC code.
033: */
034: public class SerializabilityUtil {
035:
036: public static final String DEFAULT_ENCODING = "UTF-8";
037:
038: /**
039: * Comparator used to sort fields.
040: */
041: public static final Comparator<Field> FIELD_COMPARATOR = new Comparator<Field>() {
042: public int compare(Field f1, Field f2) {
043: return f1.getName().compareTo(f2.getName());
044: }
045: };
046:
047: /**
048: * A permanent cache of all computed CRCs on classes. This is safe to do
049: * because a Class is guaranteed not to change within the lifetime of a
050: * ClassLoader (and thus, this Map). Access must be synchronized.
051: */
052: private static final Map<Class<?>, String> classCRC32Cache = new IdentityHashMap<Class<?>, String>();
053:
054: /**
055: * A permanent cache of all serializable fields on classes. This is safe to do
056: * because a Class is guaranteed not to change within the lifetime of a
057: * ClassLoader (and thus, this Map). Access must be synchronized.
058: */
059: private static final Map<Class<?>, Field[]> classSerializableFieldsCache = new IdentityHashMap<Class<?>, Field[]>();
060:
061: /**
062: * A permanent cache of all which classes onto custom field serializers. This
063: * is safe to do because a Class is guaranteed not to change within the
064: * lifetime of a ClassLoader (and thus, this Map). Access must be
065: * synchronized.
066: */
067: private static final Map<Class<?>, Class<?>> classCustomSerializerCache = new IdentityHashMap<Class<?>, Class<?>>();
068:
069: private static final String JRE_SERIALIZER_PACKAGE = "com.google.gwt.user.client.rpc.core";
070:
071: private static final Map<String, String> SERIALIZED_PRIMITIVE_TYPE_NAMES = new HashMap<String, String>();
072:
073: private static final Set<Class<?>> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES = new HashSet<Class<?>>();
074:
075: static {
076:
077: SERIALIZED_PRIMITIVE_TYPE_NAMES.put(boolean.class.getName(),
078: "Z");
079: SERIALIZED_PRIMITIVE_TYPE_NAMES.put(byte.class.getName(), "B");
080: SERIALIZED_PRIMITIVE_TYPE_NAMES.put(char.class.getName(), "C");
081: SERIALIZED_PRIMITIVE_TYPE_NAMES
082: .put(double.class.getName(), "D");
083: SERIALIZED_PRIMITIVE_TYPE_NAMES.put(float.class.getName(), "F");
084: SERIALIZED_PRIMITIVE_TYPE_NAMES.put(int.class.getName(), "I");
085: SERIALIZED_PRIMITIVE_TYPE_NAMES.put(long.class.getName(), "J");
086: SERIALIZED_PRIMITIVE_TYPE_NAMES.put(short.class.getName(), "S");
087:
088: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
089: .add(Boolean.class);
090: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
091: .add(Byte.class);
092: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
093: .add(Character.class);
094: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
095: .add(Double.class);
096: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
097: .add(Exception.class);
098: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
099: .add(Float.class);
100: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
101: .add(Integer.class);
102: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
103: .add(Long.class);
104: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
105: .add(Object.class);
106: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
107: .add(Short.class);
108: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
109: .add(String.class);
110: TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
111: .add(Throwable.class);
112: }
113:
114: public static Field[] applyFieldSerializationPolicy(Class<?> clazz) {
115: Field[] serializableFields = getCachedSerializableFieldsForClass(clazz);
116: if (serializableFields == null) {
117: ArrayList<Field> fieldList = new ArrayList<Field>();
118: Field[] fields = clazz.getDeclaredFields();
119: for (Field field : fields) {
120: assert (field != null);
121:
122: int fieldModifiers = field.getModifiers();
123: if (Modifier.isStatic(fieldModifiers)
124: || Modifier.isTransient(fieldModifiers)
125: || Modifier.isFinal(fieldModifiers)) {
126: continue;
127: }
128:
129: fieldList.add(field);
130: }
131:
132: serializableFields = fieldList.toArray(new Field[fieldList
133: .size()]);
134:
135: // sort the fields by name
136: Arrays.sort(serializableFields, 0,
137: serializableFields.length, FIELD_COMPARATOR);
138:
139: putCachedSerializableFieldsForClass(clazz,
140: serializableFields);
141: }
142:
143: return serializableFields;
144: }
145:
146: public static SerializedInstanceReference decodeSerializedInstanceReference(
147: String encodedSerializedInstanceReference) {
148: final String[] components = encodedSerializedInstanceReference
149: .split(SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR);
150: return new SerializedInstanceReference() {
151: public String getName() {
152: return components.length > 0 ? components[0] : "";
153: }
154:
155: public String getSignature() {
156: return components.length > 1 ? components[1] : "";
157: }
158: };
159: }
160:
161: public static String encodeSerializedInstanceReference(
162: Class<?> instanceType) {
163: return instanceType.getName()
164: + SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR
165: + getSerializationSignature(instanceType);
166: }
167:
168: public static String getSerializationSignature(Class<?> instanceType) {
169: String result = getCachedCRCForClass(instanceType);
170: if (result == null) {
171: CRC32 crc = new CRC32();
172: try {
173: generateSerializationSignature(instanceType, crc);
174: } catch (UnsupportedEncodingException e) {
175: throw new RuntimeException(
176: "Could not compute the serialization signature",
177: e);
178: }
179: result = Long.toString(crc.getValue());
180: putCachedCRCForClass(instanceType, result);
181: }
182: return result;
183: }
184:
185: public static String getSerializedTypeName(Class<?> instanceType) {
186: if (instanceType.isPrimitive()) {
187: return SERIALIZED_PRIMITIVE_TYPE_NAMES.get(instanceType
188: .getName());
189: }
190:
191: return instanceType.getName();
192: }
193:
194: /**
195: * Returns the {@link Class} which can serialize the given instance type. Note
196: * that arrays never have custom field serializers.
197: */
198: public static Class<?> hasCustomFieldSerializer(
199: Class<?> instanceType) {
200: assert (instanceType != null);
201: if (instanceType.isArray()) {
202: return null;
203: }
204:
205: Class<?> result = getCachedSerializerForClass(instanceType);
206: if (result != null) {
207: // this class has a custom serializer
208: return result;
209: }
210: if (containsCachedSerializerForClass(instanceType)) {
211: // this class definitely has no custom serializer
212: return null;
213: }
214: // compute whether this class has a custom serializer
215: result = computeHasCustomFieldSerializer(instanceType);
216: putCachedSerializerForClass(instanceType, result);
217: return result;
218: }
219:
220: /**
221: * This method treats arrays in a special way.
222: */
223: private static Class<?> computeHasCustomFieldSerializer(
224: Class<?> instanceType) {
225: assert (instanceType != null);
226: String qualifiedTypeName = instanceType.getName();
227: ClassLoader classLoader = Thread.currentThread()
228: .getContextClassLoader();
229: String simpleSerializerName = qualifiedTypeName
230: + "_CustomFieldSerializer";
231: Class<?> customSerializer = getCustomFieldSerializer(
232: classLoader, simpleSerializerName);
233: if (customSerializer != null) {
234: return customSerializer;
235: }
236:
237: // Try with the regular name
238: Class<?> customSerializerClass = getCustomFieldSerializer(
239: classLoader, JRE_SERIALIZER_PACKAGE + "."
240: + simpleSerializerName);
241: if (customSerializerClass != null) {
242: return customSerializerClass;
243: }
244:
245: return null;
246: }
247:
248: private static boolean containsCachedSerializerForClass(
249: Class<?> instanceType) {
250: synchronized (classCustomSerializerCache) {
251: return classCustomSerializerCache.containsKey(instanceType);
252: }
253: }
254:
255: private static boolean excludeImplementationFromSerializationSignature(
256: Class<?> instanceType) {
257: if (TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
258: .contains(instanceType)) {
259: return true;
260: }
261: return false;
262: }
263:
264: private static void generateSerializationSignature(
265: Class<?> instanceType, CRC32 crc)
266: throws UnsupportedEncodingException {
267: crc.update(getSerializedTypeName(instanceType).getBytes(
268: DEFAULT_ENCODING));
269:
270: if (excludeImplementationFromSerializationSignature(instanceType)) {
271: return;
272: }
273:
274: Class<?> customSerializer = hasCustomFieldSerializer(instanceType);
275: if (customSerializer != null) {
276: generateSerializationSignature(customSerializer, crc);
277: } else if (instanceType.isArray()) {
278: generateSerializationSignature(instanceType
279: .getComponentType(), crc);
280: } else if (!instanceType.isPrimitive()) {
281: Field[] fields = applyFieldSerializationPolicy(instanceType);
282: for (Field field : fields) {
283: assert (field != null);
284:
285: crc.update(field.getName().getBytes(DEFAULT_ENCODING));
286: crc.update(getSerializedTypeName(field.getType())
287: .getBytes(DEFAULT_ENCODING));
288: }
289:
290: Class<?> super Class = instanceType.getSuperclass();
291: if (super Class != null) {
292: generateSerializationSignature(super Class, crc);
293: }
294: }
295: }
296:
297: private static String getCachedCRCForClass(Class<?> instanceType) {
298: synchronized (classCRC32Cache) {
299: return classCRC32Cache.get(instanceType);
300: }
301: }
302:
303: private static Field[] getCachedSerializableFieldsForClass(
304: Class<?> clazz) {
305: synchronized (classSerializableFieldsCache) {
306: return classSerializableFieldsCache.get(clazz);
307: }
308: }
309:
310: private static Class<?> getCachedSerializerForClass(
311: Class<?> instanceType) {
312: synchronized (classCustomSerializerCache) {
313: return classCustomSerializerCache.get(instanceType);
314: }
315: }
316:
317: private static Class<?> getCustomFieldSerializer(
318: ClassLoader classLoader, String qualifiedSerialzierName) {
319: try {
320: Class<?> customSerializerClass = Class.forName(
321: qualifiedSerialzierName, false, classLoader);
322: return customSerializerClass;
323: } catch (ClassNotFoundException e) {
324: return null;
325: }
326: }
327:
328: private static void putCachedCRCForClass(Class<?> instanceType,
329: String crc32) {
330: synchronized (classCRC32Cache) {
331: classCRC32Cache.put(instanceType, crc32);
332: }
333: }
334:
335: private static void putCachedSerializableFieldsForClass(
336: Class<?> clazz, Field[] serializableFields) {
337: synchronized (classSerializableFieldsCache) {
338: classSerializableFieldsCache.put(clazz, serializableFields);
339: }
340: }
341:
342: private static void putCachedSerializerForClass(
343: Class<?> instanceType, Class<?> customFieldSerializer) {
344: synchronized (classCustomSerializerCache) {
345: classCustomSerializerCache.put(instanceType,
346: customFieldSerializer);
347: }
348: }
349: }
|