| /* |
| * 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; |
| |
| import com.google.gwt.user.server.rpc.impl.StandardSerializationPolicy; |
| import com.google.gwt.user.server.rpc.impl.TypeNameObfuscator; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * API for loading a {@link SerializationPolicy}. |
| */ |
| public final class SerializationPolicyLoader { |
| |
| /** |
| * Keyword for listing the serializable fields of an enchanced class that are |
| * visible to client code. |
| */ |
| public static final String CLIENT_FIELDS_KEYWORD = "@ClientFields"; |
| |
| /** |
| * Default encoding for serialization policy files. |
| */ |
| public static final String SERIALIZATION_POLICY_FILE_ENCODING = "UTF-8"; |
| |
| private static final String FORMAT_ERROR_MESSAGE = "Expected: className, " |
| + "[true | false], [true | false], [true | false], [true | false], typeId, signature"; |
| |
| /** |
| * Returns the serialization policy file name from the serialization |
| * policy strong name. |
| * |
| * @param serializationPolicyStrongName the serialization policy strong name |
| * @return the serialization policy file name from the serialization |
| * policy strong name |
| */ |
| public static String getSerializationPolicyFileName( |
| String serializationPolicyStrongName) { |
| return serializationPolicyStrongName + ".gwt.rpc"; |
| } |
| |
| /** |
| * Loads a SerializationPolicy from an input stream. |
| * |
| * @param inputStream stream to load from |
| * @return a {@link SerializationPolicy} loaded from the input stream |
| * |
| * @throws IOException if an error occurs while reading the stream |
| * @throws ParseException if the input stream is not properly formatted |
| * @throws ClassNotFoundException if a class specified in the serialization |
| * policy cannot be loaded |
| * |
| * @deprecated see {@link #loadFromStream(InputStream, List)} |
| */ |
| @Deprecated |
| public static SerializationPolicy loadFromStream(InputStream inputStream) |
| throws IOException, ParseException, ClassNotFoundException { |
| List<ClassNotFoundException> classNotFoundExceptions = new ArrayList<ClassNotFoundException>(); |
| SerializationPolicy serializationPolicy = loadFromStream(inputStream, |
| classNotFoundExceptions); |
| if (!classNotFoundExceptions.isEmpty()) { |
| // Just report the first failure. |
| throw classNotFoundExceptions.get(0); |
| } |
| |
| return serializationPolicy; |
| } |
| |
| /** |
| * Loads a SerializationPolicy from an input stream and optionally record any |
| * {@link ClassNotFoundException}s. |
| * |
| * @param inputStream stream to load the SerializationPolicy from. |
| * @param classNotFoundExceptions if not <code>null</code>, all of the |
| * {@link ClassNotFoundException}s thrown while loading this |
| * serialization policy will be added to this list |
| * @return a {@link SerializationPolicy} loaded from the input stream. |
| * |
| * @throws IOException if an error occurs while reading the stream |
| * @throws ParseException if the input stream is not properly formatted |
| */ |
| public static SerializationPolicy loadFromStream(InputStream inputStream, |
| List<ClassNotFoundException> classNotFoundExceptions) throws IOException, |
| ParseException { |
| |
| if (inputStream == null) { |
| throw new NullPointerException("inputStream"); |
| } |
| |
| Map<Class<?>, Boolean> whitelistSer = new HashMap<Class<?>, Boolean>(); |
| Map<Class<?>, Boolean> whitelistDeser = new HashMap<Class<?>, Boolean>(); |
| Map<Class<?>, String> typeIds = new HashMap<Class<?>, String>(); |
| Map<Class<?>, Set<String>> clientFields = new HashMap<Class<?>, Set<String>>(); |
| |
| ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); |
| |
| InputStreamReader isr = new InputStreamReader(inputStream, |
| SERIALIZATION_POLICY_FILE_ENCODING); |
| BufferedReader br = new BufferedReader(isr); |
| |
| String line = br.readLine(); |
| int lineNum = 1; |
| while (line != null) { |
| line = line.trim(); |
| if (line.length() > 0) { |
| String[] components = line.split(","); |
| |
| if (components[0].equals(CLIENT_FIELDS_KEYWORD)) { |
| /* |
| * Lines starting with '@ClientFields' list potentially serializable fields known to |
| * client code for classes that may be enhanced with additional fields on the server. |
| * If additional server fields are found, they will be serizalized separately from the |
| * normal RPC process and transmitted to the client as an opaque blob of data stored |
| * in a WeakMapping associated with the object instance. |
| */ |
| String binaryTypeName = components[1].trim(); |
| Class<?> clazz; |
| try { |
| clazz = Class.forName(binaryTypeName, false, contextClassLoader); |
| HashSet<String> fieldNames = new HashSet<String>(); |
| for (int i = 2; i < components.length; i++) { |
| fieldNames.add(components[i]); |
| } |
| clientFields.put(clazz, fieldNames); |
| } catch (ClassNotFoundException ex) { |
| // Ignore the error, but add it to the list of errors if one was |
| // provided. |
| if (classNotFoundExceptions != null) { |
| classNotFoundExceptions.add(ex); |
| } |
| } |
| } else { |
| if (components.length != 2 && components.length != 7) { |
| throw new ParseException(FORMAT_ERROR_MESSAGE, lineNum); |
| } |
| |
| for (int i = 0; i < components.length; i++) { |
| components[i] = components[i].trim(); |
| if (components[i].length() == 0) { |
| throw new ParseException(FORMAT_ERROR_MESSAGE, lineNum); |
| } |
| } |
| |
| String binaryTypeName = components[0].trim(); |
| boolean fieldSer; |
| boolean instantSer; |
| boolean fieldDeser; |
| boolean instantDeser; |
| String typeId; |
| |
| if (components.length == 2) { |
| fieldSer = fieldDeser = true; |
| instantSer = instantDeser = Boolean.valueOf(components[1]); |
| typeId = binaryTypeName; |
| } else { |
| int idx = 1; |
| // TODO: Validate the instantiable string better. |
| fieldSer = Boolean.valueOf(components[idx++]); |
| instantSer = Boolean.valueOf(components[idx++]); |
| fieldDeser = Boolean.valueOf(components[idx++]); |
| instantDeser = Boolean.valueOf(components[idx++]); |
| typeId = components[idx++]; |
| |
| if (!fieldSer && !fieldDeser |
| && !TypeNameObfuscator.SERVICE_INTERFACE_ID.equals(typeId)) { |
| throw new ParseException("Type " + binaryTypeName |
| + " is neither field serializable, field deserializable " |
| + "nor the service interface", lineNum); |
| } |
| } |
| |
| try { |
| Class<?> clazz = Class.forName(binaryTypeName, false, |
| contextClassLoader); |
| if (fieldSer) { |
| whitelistSer.put(clazz, instantSer); |
| } |
| if (fieldDeser) { |
| whitelistDeser.put(clazz, instantDeser); |
| } |
| typeIds.put(clazz, typeId); |
| } catch (ClassNotFoundException ex) { |
| // Ignore the error, but add it to the list of errors if one was |
| // provided. |
| if (classNotFoundExceptions != null) { |
| classNotFoundExceptions.add(ex); |
| } |
| } |
| } |
| } |
| |
| line = br.readLine(); |
| lineNum++; |
| } |
| |
| return new StandardSerializationPolicy(whitelistSer, whitelistDeser, |
| typeIds, clientFields); |
| } |
| |
| private SerializationPolicyLoader() { |
| } |
| } |