/***************************************************
* *
* Mobicents: The Open Source JSLEE Platform *
* *
* Distributable under LGPL license. *
* See terms of license at gnu.org. *
* *
***************************************************/
/*
* ConcreteClassGeneratorUtils.java
*
* Created on Aug 19, 2004
*
*/
package org.mobicents.slee.container.deployment;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import javax.slee.SLEEException;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.SleeContainerUtils;
/**
* Utility class to generate concrete classes
*
* @author DERUELLE Jean <a
* href="mailto:jean.deruelle@gmail.com">jean.deruelle@gmail.com </a>
* @author <a href="mailto:michele.laporta@gmail.com">Michele La Porta</a>
*/
public class ConcreteClassGeneratorUtils {
public static Logger logger = null;
public static final String SBB_CONCRETE_CLASS_NAME_PREFIX = "";
public static final String SBB_CONCRETE_CLASS_NAME_SUFFIX = "Impl";
public static final String CONCRETE_ACTIVITY_INTERFACE_CLASS_NAME_PREFIX = "";
public static final String CONCRETE_ACTIVITY_INTERFACE_CLASS_NAME_SUFFIX = "Impl";
public static final String SBB_LOCAL_OBJECT_CLASS_NAME_PREFIX = "";
public static final String SBB_LOCAL_OBJECT_CLASS_NAME_SUFFIX = "Impl";
public static final String PROFILE_CONCRETE_CLASS_NAME_PREFIX = "";
public static final String PROFILE_CONCRETE_CLASS_NAME_SUFFIX = "Impl";
public static final String PROFILE_MBEAN_CONCRETE_CLASS_NAME_PREFIX = "";
public static final String PROFILE_MBEAN_CONCRETE_CLASS_NAME_SUFFIX = "MBeanImpl";
public static final String PROFILE_TRANSIENT_CLASS_NAME_PREFIX = "";
public static final String PROFILE_TRANSIENT_CLASS_NAME_SUFFIX = "TransientState";
public static final String SBB_USAGE_PARAMETERS_INTERFACE_PREFIX = "";
public static final String SBB_USAGE_PARAMETERS_INTERFACE_SUFFIX = "Impl";
public static final String PROFILE_LOCAL_CLASS_NAME_PREFIX = "";
public static final String PROFILE_LOCAL_CLASS_NAME_SUFFIX = "LocalImpl";
public static final String PROFILE_TABLE_CLASS_NAME_PREFIX = "";
public static final String PROFILE_TABLE_CLASS_NAME_SUFFIX = "TableImpl";
// The root class pool
static {
logger = Logger.getLogger(ConcreteClassGeneratorUtils.class);
}
/**
* Recursively walk a directory tree and return a List of all jars Files found;
* the List is sorted using File.compareTo.
*
* @param aStartingDir
* is a valid directory, which can be read.
*/
static public List getJarsFileListing(File aStartingDir)
throws FileNotFoundException {
validateDirectory(aStartingDir);
List result = new ArrayList();
File[] filesAndDirs = aStartingDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
if(pathname.getName().endsWith(".jar"))
return true;
return false;
}
});
List filesDirs = Arrays.asList(filesAndDirs);
Iterator filesIter = filesDirs.iterator();
File file = null;
while (filesIter.hasNext()) {
file = (File) filesIter.next();
result.add(file); // always add, even if directory
if (!file.isFile()) {
// must be a directory
// recursive call!
List deeperList = getJarsFileListing(file);
result.addAll(deeperList);
}
}
Collections.sort(result);
return result;
}
/**
* Directory is valid if it exists, does not represent a file, and can be read.
*/
static private void validateDirectory(File aDirectory)
throws FileNotFoundException {
if (aDirectory == null) {
throw new IllegalArgumentException("Directory should not be null.");
}
if (!aDirectory.exists()) {
throw new FileNotFoundException("Directory does not exist: "
+ aDirectory);
}
if (!aDirectory.isDirectory()) {
throw new IllegalArgumentException("Is not a directory: "
+ aDirectory);
}
if (!aDirectory.canRead()) {
throw new IllegalArgumentException("Directory cannot be read: "
+ aDirectory);
}
}
/**
*
* Create a concrete sbb class name based on a given sbb abstract class name
*
* @param sbbAbstractClassName the sbb abstract name which will be the base of the concrete class
*/
public static String getSbbConcreteClassName(String sbbAbstractClassName)
{
return
ConcreteClassGeneratorUtils.SBB_CONCRETE_CLASS_NAME_PREFIX
+ sbbAbstractClassName
+ ConcreteClassGeneratorUtils.SBB_CONCRETE_CLASS_NAME_SUFFIX;
}
/**
* Create the links with possible interfaces
*
* @param concreteClass
* the concrete class to which to add the interface links
* @param interfaces
* the interfaces the concrete class has to implement
*/
public static void createInterfaceLinks(CtClass concreteClass,
CtClass[] interfaces) {
if (interfaces != null)
concreteClass.setInterfaces(interfaces);
else
return;
for (int i = 0; i < interfaces.length; i++) {
logger.debug(concreteClass.getName() + " Implements link with "
+ interfaces[i].getName() + " interface created");
}
}
/**
* Create the inheritance link with the sbb absract class provided by the
* sbb developer
*
* @param concreteClass
* the concrete class to which to add the inheritance link
* @param superClass
* the superClass to set
*/
public static void createInheritanceLink(CtClass concreteClass,
CtClass superClass) {
if (superClass == null)
return;
try {
concreteClass.setSuperclass(superClass);
logger.debug(concreteClass.getName() + " Inheritance link with "
+ superClass.getName() + " class created");
} catch (CannotCompileException cce) {
cce.printStackTrace();
}
}
/**
* Add a concrete method invoking the interceptor <B>interceptorName </B>
* with the same parameters,name,return type as the method <B>method </B>
*
* @param concreteClass
* the concrete where to add the interceptor method
* @param method
* method from which is generating the new proxy method
* @param interceptorName
* the interceptor name implementing the interface
* InvocationHandler. This name must be the name of an existing
* field of the concrete class
* @param callSuperMethod
* if true the concrete implementation of the method will call
* super()
*/
public static void addInterceptedMethod(CtClass concreteClass,
CtMethod method, String interceptorName, boolean callSuperMethod) {
if (method == null) throw new InvalidParameterException("Intercepted method should not be null");
if (interceptorName == null) throw new InvalidParameterException("Interceptor class name should not be null");
String methodToAdd = "public ";
//Add the return type
boolean hasReturn = false;
CtClass returnType = null;
try {
returnType = method.getReturnType();
methodToAdd = methodToAdd.concat(returnType.getName() + " ");
hasReturn = true;
} catch (NotFoundException nfe) {
//nfe.printStackTrace();
logger.debug("No return type -- assuming return type is void " );
methodToAdd = methodToAdd + "void ";
}
//Add the method name
methodToAdd = methodToAdd.concat(method.getName() + "(");
//Add the parameters
CtClass[] parameterTypes = null;
;
String parametersInit = "Object[] args=null;";
String argsInit = "Class[] classes=null;";
try {
parameterTypes = method.getParameterTypes();
parametersInit = parametersInit + "args=new Object["
+ parameterTypes.length + "];";
argsInit = argsInit + "classes=new Class[" + parameterTypes.length
+ "];";
for (int argNumber = 0; argNumber < parameterTypes.length; argNumber++) {
methodToAdd = methodToAdd.concat(parameterTypes[argNumber]
.getName()
+ " arg_" + argNumber);
//handle the primitive types
if (!parameterTypes[argNumber].isPrimitive())
parametersInit = parametersInit + " args[" + argNumber
+ "]=arg_" + argNumber + ";";
else
parametersInit = parametersInit
+ " args["
+ argNumber
+ "]="
+ ClassUtils
.getObjectFromPrimitiveType(
parameterTypes[argNumber].getName(),
"arg_" + argNumber) + ";";
String typeClass = parameterTypes[argNumber].getName();
//handle the primitive types
if (!parameterTypes[argNumber].isPrimitive()) {
if (parameterTypes[argNumber].isArray()) {
String arrayClassRepresentation = toArray(parameterTypes[argNumber]);
if (arrayClassRepresentation != null)
argsInit = argsInit + "classes[" + argNumber + "]="
+ SleeContainerUtils.class.getName() + ".getCurrentThreadClassLoader().loadClass(\""
+ arrayClassRepresentation + "\");";
} else
argsInit = argsInit + "classes[" + argNumber
+ "]="+SleeContainerUtils.class.getName() + ".getCurrentThreadClassLoader().loadClass(\"" + typeClass + "\");";
} else
argsInit = argsInit + "classes[" + argNumber + "]="
+ ClassUtils.getClassFromPrimitiveType(typeClass) + ".TYPE;";
if (argNumber + 1 < parameterTypes.length)
methodToAdd = methodToAdd + ",";
}
methodToAdd += ") ";
// Add method exceptions
if (method.getExceptionTypes().length > 0)
{
CtClass[] exceptions = method.getExceptionTypes();
methodToAdd += " throws ";
for (int i = 0; i < exceptions.length-1; i++) {
String exName = exceptions[i].getName();
methodToAdd += exName + ", ";
}
methodToAdd += exceptions[exceptions.length-1].getName();
}
} catch (NotFoundException nfe) {
nfe.printStackTrace();
throw new SLEEException("Failed creating concrete Profile MBean implementation class", nfe);
}
// Start adding method body
methodToAdd += " { ";
methodToAdd += "" + parametersInit;
methodToAdd += "" + argsInit;
methodToAdd += "Class clazz=this.getClass();";
methodToAdd += "Object result=null;";
methodToAdd += "try{";
//call the super method
if (callSuperMethod) {
if(method.getName().equals("profileStore")){
methodToAdd = methodToAdd + "super." + method.getName() + "(";
if (parameterTypes != null && parameterTypes.length > 0) {
for (int argNumber = 0; argNumber < parameterTypes.length; argNumber++) {
methodToAdd = methodToAdd + "arg_" + argNumber;
if (argNumber + 1 < parameterTypes.length)
methodToAdd = methodToAdd + ",";
}
}
methodToAdd = methodToAdd + ");";
}
}
methodToAdd = methodToAdd
+ "java.lang.reflect.Method method=clazz.getDeclaredMethod(\""
+ method.getName() + "\",classes" + ");";
methodToAdd = methodToAdd + "result=" + interceptorName
+ ".invoke(this,method,args); ";
methodToAdd = methodToAdd
+ "}catch(RuntimeException t){t.printStackTrace(); throw (t); "
+ " } catch (Exception ex1) { ex1.printStackTrace(); throw (ex1); }";
//call the super method
if (callSuperMethod) {
if(!method.getName().equals("profileStore")){
methodToAdd = methodToAdd + "super." + method.getName() + "(";
if (parameterTypes != null && parameterTypes.length > 0) {
for (int argNumber = 0; argNumber < parameterTypes.length; argNumber++) {
methodToAdd = methodToAdd + "arg_" + argNumber;
if (argNumber + 1 < parameterTypes.length)
methodToAdd += ",";
}
}
methodToAdd += ");";
}
}
//handle the primitive types
if (hasReturn) {
if (!returnType.getName().equalsIgnoreCase("void")) {
if (!returnType.isPrimitive())
methodToAdd = methodToAdd + "return ("
+ returnType.getName() + ")result;";
else
methodToAdd = methodToAdd
+ "return "
+ ClassUtils
.getPrimitiveTypeFromObject(returnType
.getName(), "result") + ";";
}
}
methodToAdd += "}";
//Add the implementation code
logger.debug("Method " + methodToAdd + " added");
CtMethod methodTest;
try {
methodTest = CtNewMethod.make(methodToAdd, concreteClass);
concreteClass.addMethod(methodTest);
} catch (CannotCompileException cce) {
throw new SLEEException("Cannot compile method " + method.getName(), cce);
}
}
/**
* Transform an array class to its string representation <BR>
* example: String[] -> [Ljava.lang.String;
*
* @param typeClass
* the array class to transform
* @return the String representation of the class
*/
public static String toArray(CtClass typeClass) {
StringTokenizer st = new StringTokenizer(typeClass.getName(), "[");
String name = null;
CtClass arrayClass;
try {
arrayClass = typeClass.getComponentType();
if (!arrayClass.isPrimitive())
name = "L" + arrayClass.getName().replace('/', '.') + ";";
else
name = toJvmRepresentation(arrayClass.getName());
st.nextToken();
while (st.hasMoreTokens()) {
st.nextToken();
name = "[" + name;
}
} catch (NotFoundException e) {
e.printStackTrace();
}
return name;
}
/**
* Get the jvm representation of the primitives types <BR>
* Exemple: int -> I, boolean -> Z and so on...
*
*
* @param primitiveTypeName
* the primitive type name whose the jvm representation is wanted
* @return the jvm representation of the primitive type
*/
public static String toJvmRepresentation(String primitiveTypeName) {
if (primitiveTypeName.equals("int"))
return "I";
if (primitiveTypeName.equals("boolean"))
return "Z";
if (primitiveTypeName.equals("byte"))
return "B";
if (primitiveTypeName.equals("char"))
return "C";
if (primitiveTypeName.equals("double"))
return "D";
if (primitiveTypeName.equals("float"))
return "F";
if (primitiveTypeName.equals("long"))
return "J";
if (primitiveTypeName.equals("short"))
return "S";
if (primitiveTypeName.equals("void"))
return "V";
return primitiveTypeName;
}
/**
* Copy declared methods from one class to another
*
* @param source
* the class from which the methods are copied
* @param destination
* the class to which the methods are copied
* @param exceptions
* optionnal, defines the set of exceptions the methods can throw
*/
public static void copyMethods(CtClass source, CtClass destination,
CtClass[] exceptions) {
copyMethods(source.getDeclaredMethods(), destination, exceptions);
}
/**
* Copy all methods from one class to another
* FIXME emmartins: merge with other method when 1.1 dev cycle ends
* @param source
* the class from which the methods are copied
* @param destination
* the class to which the methods are copied
* @param exceptions
* optionnal, defines the set of exceptions the methods can throw
*/
public static void copyAllMethods(CtClass source, CtClass destination,
CtClass[] exceptions) {
copyMethods(source.getDeclaredMethods(), destination, exceptions);
}
/**
* Copy methods to a class
*
* @param methods
* the methods to copy
* @param destination
* the class to which the methods are copied
* @param exceptions
* optional, defines the set of exceptions the methods can throw
*/
public static void copyMethods(CtMethod[] methods, CtClass destination,
CtClass[] exceptions) {
CtMethod methodCopy = null;
for (CtMethod method : methods) {
try {
methodCopy = new CtMethod(method, destination, null);
if (exceptions != null) {
try {
methodCopy.setExceptionTypes(exceptions);
} catch (NotFoundException e) {
throw new SLEEException(e.getMessage(),e);
}
}
destination.addMethod(methodCopy);
} catch (CannotCompileException e) {
throw new SLEEException(e.getMessage(),e);
}
}
}
}