| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 flex.messaging.util; |
| |
| import flex.messaging.MessageException; |
| import flex.messaging.io.SerializationContext; |
| import flex.messaging.io.SerializationException; |
| import flex.messaging.validators.DeserializationValidator; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Modifier; |
| |
| /** |
| * Utility class to create instances of Complex Types |
| * and handle error conditions consistently across the RemoteObject |
| * code base. |
| * |
| * |
| */ |
| public class ClassUtil |
| { |
| private static final int TYPE_NOT_FOUND = 10008; |
| private static final int UNEXPECTED_TYPE = 10009; |
| private static final int CANNOT_CREATE_TYPE = 10010; |
| private static final int SECURITY_ERROR = 10011; |
| private static final int UNKNOWN_ERROR = 10012; |
| |
| private static final String NULL = "null"; |
| |
| private ClassUtil() |
| { |
| } |
| |
| /** |
| * Create a class of the specified type. |
| * Use {@link #createClass(String, ClassLoader)} and explicitly provide the MessageBroker class loader |
| * |
| * @param type fully qualified class name |
| * @return the class instance |
| */ |
| public static Class createClass(String type) |
| { |
| return createClass(type, null); |
| } |
| |
| /** |
| * Create a class of the specified type using the provided class loader. |
| * @param type fully qualified class name |
| * @param loader class loader, if null the class loader that loaded ClassUtil is used |
| * @return the class instance |
| */ |
| public static Class createClass(String type, ClassLoader loader) |
| { |
| try |
| { |
| if (type != null) |
| type = type.trim(); |
| |
| if (loader == null) // will use the loader for this class |
| return Class.forName(type); |
| return Class.forName(type, true, loader); |
| } |
| catch (ClassNotFoundException cnf) |
| { |
| // Cannot invoke type '{type}' |
| MessageException ex = new MessageException(); |
| ex.setMessage(TYPE_NOT_FOUND, new Object[] {type}); |
| ex.setDetails(TYPE_NOT_FOUND, "0", new Object[] {type}); |
| ex.setCode(MessageException.CODE_SERVER_RESOURCE_UNAVAILABLE); |
| throw ex; |
| } |
| } |
| |
| /** |
| * Creates the default instance of the class and verifies that it matches |
| * with the expected class type, if one passed in. |
| * |
| * @param cls The class to create. |
| * @param expectedInstance The expected class type. |
| * @return The default instance of the class. |
| */ |
| public static Object createDefaultInstance(Class cls, Class expectedInstance) |
| { |
| return ClassUtil.createDefaultInstance(cls, expectedInstance, false /*validate*/); |
| } |
| |
| /** |
| * Creates the default instance of the class and verifies that it matches |
| * with the expected class type, if one passed in. It also validates the creation |
| * of the instance with the deserialization validator, if one exists. |
| * |
| * @param cls The class to create. |
| * @param expectedInstance The expected class type. |
| * @param validate Controls whether the creation of the instance is validated |
| * with the deserialization validator. |
| * @return The default instance of the class. |
| */ |
| public static Object createDefaultInstance(Class cls, Class expectedInstance, boolean validate) |
| { |
| if (validate) |
| validateCreation(cls); |
| |
| String type = cls.getName(); |
| try |
| { |
| Object instance = cls.newInstance(); |
| |
| if (expectedInstance != null && !expectedInstance.isInstance(instance)) |
| { |
| // Given type '{name}' is not of expected type '{expectedName}'. |
| MessageException ex = new MessageException(); |
| ex.setMessage(UNEXPECTED_TYPE, new Object[] {instance.getClass().getName(), expectedInstance.getName()}); |
| ex.setCode(MessageException.CODE_SERVER_RESOURCE_UNAVAILABLE); |
| throw ex; |
| } |
| |
| return instance; |
| } |
| catch (IllegalAccessException ia) |
| { |
| boolean details = false; |
| StringBuffer message = new StringBuffer("Unable to create a new instance of type "); |
| message.append(type); |
| |
| //Look for a possible cause... |
| |
| // Class might not have a suitable constructor? |
| if (!hasValidDefaultConstructor(cls)) |
| { |
| details = true; |
| } |
| |
| // Unable to create a new instance of type '{type}'. |
| MessageException ex = new MessageException(); |
| ex.setMessage(CANNOT_CREATE_TYPE, new Object[] {type}); |
| if (details) |
| { |
| //Types must have a public, no arguments constructor |
| ex.setDetails(CANNOT_CREATE_TYPE, "0"); |
| } |
| ex.setCode(MessageException.CODE_SERVER_RESOURCE_UNAVAILABLE); |
| throw ex; |
| } |
| catch (InstantiationException ine) |
| { |
| String variant = null; |
| |
| //Look for a possible cause... |
| |
| if (cls.isInterface()) // Class is really an interface? |
| variant = "1"; // Interfaces cannot be instantiated |
| else if (isAbstract(cls)) |
| variant = "2"; //Abstract types cannot be instantiated. |
| else if (!hasValidDefaultConstructor(cls)) // Class might not have a suitable constructor? |
| variant = "3"; // Types cannot be instantiated without a public, no arguments constructor. |
| |
| MessageException ex = new MessageException(); |
| ex.setMessage(CANNOT_CREATE_TYPE, new Object[] {type}); |
| if (variant != null) |
| ex.setDetails(CANNOT_CREATE_TYPE, variant); |
| ex.setCode(MessageException.CODE_SERVER_RESOURCE_UNAVAILABLE); |
| throw ex; |
| } |
| catch (SecurityException se) |
| { |
| MessageException ex = new MessageException(); |
| ex.setMessage(SECURITY_ERROR, new Object[] {type}); |
| ex.setCode(MessageException.CODE_SERVER_RESOURCE_UNAVAILABLE); |
| ex.setRootCause(se); |
| throw ex; |
| } |
| catch (MessageException me) |
| { |
| throw me; |
| } |
| catch (Exception e) |
| { |
| MessageException ex = new MessageException(); |
| ex.setMessage(UNKNOWN_ERROR, new Object[] {type}); |
| ex.setCode(MessageException.CODE_SERVER_RESOURCE_UNAVAILABLE); |
| ex.setRootCause(e); |
| throw ex; |
| } |
| } |
| |
| public static boolean isAbstract(Class cls) |
| { |
| try |
| { |
| if (cls != null) |
| { |
| int mod = cls.getModifiers(); |
| return Modifier.isAbstract(mod); |
| } |
| } |
| catch (Throwable t) |
| { |
| return false; |
| } |
| |
| return false; |
| } |
| |
| public static boolean hasValidDefaultConstructor(Class cls) |
| { |
| try |
| { |
| if (cls != null) |
| { |
| Constructor c = cls.getConstructor(); |
| int mod = c.getModifiers(); |
| return Modifier.isPublic(mod); |
| } |
| } |
| catch (Throwable t) |
| { |
| return false; |
| } |
| |
| return false; |
| } |
| |
| public static String classLoaderToString(ClassLoader cl) |
| { |
| if (cl == null) |
| return NULL; |
| |
| if (cl == ClassLoader.getSystemClassLoader()) |
| return "system"; |
| |
| StringBuffer sb = new StringBuffer(); |
| sb.append("hashCode: " + System.identityHashCode(cl) + " (parent " + ClassUtil.classLoaderToString(cl.getParent()) + ")"); |
| return sb.toString(); |
| } |
| |
| /** |
| * Validates the assignment of a value to an index of an Array or List instance |
| * against the deserialization validator. If the assignment is not valid, |
| * SerializationException is thrown. |
| * |
| * @param obj The Array or List instance. |
| * @param index The index at which the value is being assigned. |
| * @param value The value that is assigned to the index. |
| */ |
| public static void validateAssignment(Object obj, int index, Object value) |
| { |
| SerializationContext context = SerializationContext.getSerializationContext(); |
| DeserializationValidator validator = context.getDeserializationValidator(); |
| if (validator == null) |
| return; |
| |
| boolean valid = true; |
| try |
| { |
| valid = validator.validateAssignment(obj, index, value); |
| } |
| catch (Exception e) |
| { |
| // Assignment validation of the object with type '{0}' for the index '{1}' failed. |
| SerializationException se = new SerializationException(); |
| se.setMessage(10313, new Object[]{obj == null? NULL : obj.getClass().getName(), index}); |
| se.setRootCause(e); |
| throw se; |
| } |
| if (!valid) |
| { |
| SerializationException se = new SerializationException(); |
| se.setMessage(10313, new Object[]{obj == null? NULL : obj.getClass().getName(), index}); |
| throw se; |
| } |
| } |
| |
| /** |
| * Validates the assignment of a property of an instance of a class to a value |
| * against the deserialization validator. If the assignment is not valid, |
| * SerializationException is thrown. |
| * |
| * @param obj The class instance whose property is being assigned to a value. |
| * @param propName The name of the property that is being assigned. |
| * @param value The value that the property is being assigned to. |
| */ |
| public static void validateAssignment(Object obj, String propName, Object value) |
| { |
| SerializationContext context = SerializationContext.getSerializationContext(); |
| DeserializationValidator validator = context.getDeserializationValidator(); |
| if (validator == null) |
| return; |
| |
| boolean valid = true; |
| try |
| { |
| valid = validator.validateAssignment(obj, propName, value); |
| } |
| catch (Exception e) |
| { |
| // Assignment validation of the object with type '{0}' for the property '{1}' failed. |
| SerializationException se = new SerializationException(); |
| se.setMessage(10312, new Object[]{obj == null? NULL : obj.getClass().getName(), propName}); |
| se.setRootCause(e); |
| throw se; |
| } |
| if (!valid) |
| { |
| SerializationException se = new SerializationException(); |
| se.setMessage(10312, new Object[]{obj == null? NULL : obj.getClass().getName(), propName}); |
| throw se; |
| } |
| } |
| |
| /** |
| * Validates the creation of the class instance against the deserialization |
| * validator, if one exists. If the class creation is not valid, |
| * SerializationException is thrown. |
| * |
| * @param cls The class to validate. |
| */ |
| public static void validateCreation(Class<?> cls) |
| { |
| SerializationContext context = SerializationContext.getSerializationContext(); |
| DeserializationValidator validator = context.getDeserializationValidator(); |
| if (validator == null) |
| return; |
| |
| boolean valid = true; |
| try |
| { |
| valid = validator.validateCreation(cls); |
| } |
| catch (Exception e) |
| { |
| // Creation validation for class '{0}' failed. |
| SerializationException se = new SerializationException(); |
| se.setMessage(10311, new Object[]{cls == null? NULL : cls.getName()}); |
| se.setRootCause(e); |
| throw se; |
| } |
| if (!valid) |
| { |
| // Creation validation for class '{0}' failed. |
| SerializationException se = new SerializationException(); |
| se.setMessage(10311, new Object[]{cls == null? NULL : cls.getName()}); |
| throw se; |
| } |
| } |
| } |