blob: c2413d0a7fb199c9f53826878bbfafa1d5b4d74b [file] [log] [blame]
/*
* 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;
}
}
}