/*************************************************** * * * Mobicents: The Open Source JSLEE Platform * * * * Distributable under LGPL license. * * See terms of license at gnu.org. * * * ***************************************************/ /* * ConcreteSbbLocalObjectGenerator.java * * Created on Dec 6, 2004 * */ package org.mobicents.slee.container.deployment; import java.io.IOException; import java.util.Iterator; import java.util.Map; import javassist.CannotCompileException; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.Modifier; import javassist.NotFoundException; import org.apache.log4j.Logger; import org.mobicents.slee.container.SleeContainerUtils; import org.mobicents.slee.container.component.deployment.ClassPool; import org.mobicents.slee.container.deployment.interceptors.SbbLocalObjectInterceptor; import org.mobicents.slee.runtime.sbb.SbbLocalObjectConcrete; import org.mobicents.slee.runtime.sbb.SbbLocalObjectImpl; import org.mobicents.slee.runtime.sbbentity.SbbEntity; /** * Class generating the concrete Sbb local object class * * @author DERUELLE Jean <a * href="mailto:jean.deruelle@gmail.com">jean.deruelle@gmail.com </a> * */ public class ConcreteSbbLocalObjectGenerator { /** * Logger to logg information */ private static Logger logger = null; private final String sbbLocalObjectName; private final String sbbAbstractClassName; private final String deployPath; /** * Pool to generate or read classes with javassist */ private final ClassPool pool; /** * */ protected CtClass sleeSbbLocalObject = null; /** * The interface from which to generate the concrete class */ protected CtClass sbbLocalObjectInterface = null; /** * The concrete sbb local object class */ protected CtClass concreteSbbLocalObject = null; static { logger = Logger.getLogger(ConcreteSbbLocalObjectGenerator.class); } public ConcreteSbbLocalObjectGenerator(String sbbLocalObjectName, String sbbAbstractClassName, String deployPath, ClassPool pool) { this.sbbLocalObjectName = sbbLocalObjectName; this.sbbAbstractClassName = sbbAbstractClassName; this.deployPath = deployPath; this.pool = pool; } /** * Generate the Sbb Local Object Class * * @param sbbLocalObjectName * the name of the Sbb Local Object * @return the concrete Sbb Local Object class implementing the Sbb Local * Object */ public Class generateSbbLocalObjectConcreteClass() { //Generates the implements link if (logger.isDebugEnabled()) { logger .debug("generateSbbLocalObjectConcreteClass: sbbLocalObjectInterface = " + sbbLocalObjectName + " deployPath = " + deployPath); } try { concreteSbbLocalObject = pool.makeClass(ConcreteClassGeneratorUtils.SBB_LOCAL_OBJECT_CLASS_NAME_PREFIX + sbbLocalObjectName + ConcreteClassGeneratorUtils.SBB_LOCAL_OBJECT_CLASS_NAME_SUFFIX); try { sleeSbbLocalObject = pool.get(SbbLocalObjectImpl.class .getName()); sbbLocalObjectInterface = pool.get(sbbLocalObjectName); } catch (NotFoundException nfe) { nfe.printStackTrace(); String s = "Problem with pool "; logger.error(s, nfe); throw new RuntimeException(s, nfe); } // This is our implementation interface. CtClass concreteClassInterface; try { concreteClassInterface = pool.get(SbbLocalObjectConcrete.class .getName()); } catch (NotFoundException nfe) { nfe.printStackTrace(); String s = "Problem with the pool! "; logger.error(s, nfe); throw new RuntimeException(s, nfe); } ConcreteClassGeneratorUtils.createInterfaceLinks( concreteSbbLocalObject, new CtClass[] { sbbLocalObjectInterface, concreteClassInterface }); //Generates an inheritance link to the slee implementation of the // SbbLocalObject interface ConcreteClassGeneratorUtils.createInheritanceLink( concreteSbbLocalObject, sleeSbbLocalObject); //Creates the constructor with parameters try { CtClass[] parameters = new CtClass[] { pool.get(SbbEntity.class .getName()) }; createConstructorWithParameter(parameters); } catch (NotFoundException nfe) { nfe.printStackTrace(); logger.error("Constructor With Parameter not created", nfe); throw new RuntimeException("Problem with the pool", nfe); } //Generates the methods to implement from the interface Map interfaceMethods = ClassUtils .getInterfaceMethodsFromInterface(sbbLocalObjectInterface); generateConcreteMethods(interfaceMethods, sbbAbstractClassName); //Generates the methods to implement from the interface generateEqualsMethod(); //generates the class generateGetSbbEntityId(); try { concreteSbbLocalObject.writeFile(deployPath); if (logger.isDebugEnabled()) { logger .debug("Concrete Class " + concreteSbbLocalObject.getName() + " generated in the following path " + deployPath); } } catch (CannotCompileException e) { String s = " Unexpected exception ! "; logger.fatal(s, e); throw new RuntimeException(s, e); } catch (IOException e) { String s = "IO Exception!"; logger.error(s, e); return null; } //load the class try { return Thread.currentThread().getContextClassLoader().loadClass(concreteSbbLocalObject.getName()); } catch (ClassNotFoundException e) { logger.error("unable to load sbb local object impl class", e); return null; } } finally { if (this.concreteSbbLocalObject != null) this.concreteSbbLocalObject.defrost(); } } /** * Creates a constructor with parameters <BR> * For every parameter a field of the same class is created in the concrete * class And each field is gonna be initialized with the corresponding * parameter * * @param parameters * the parameters of the constructor to add */ protected void createConstructorWithParameter(CtClass[] parameters) { CtConstructor constructorWithParameter = new CtConstructor(parameters, concreteSbbLocalObject); String constructorBody = "{"; for (int i = 0; i < parameters.length; i++) { String parameterName = parameters[i].getName(); parameterName = parameterName.substring(parameterName .lastIndexOf(".") + 1); String firstCharLowerCase = parameterName.substring(0, 1) .toLowerCase(); parameterName = firstCharLowerCase.concat(parameterName .substring(1)); int paramNumber = i + 1; if (((CtClass) parameters[i]).getName().equals( SbbEntity.class.getName())) { // HACK Alert -- this one better be first! constructorBody += "super($" + paramNumber + ");"; } else { try { CtField ctField = new CtField(parameters[i], parameterName, concreteSbbLocalObject); ctField.setModifiers(Modifier.PRIVATE); //concreteSbbLocalObject.addField(ctField); } catch (CannotCompileException cce) { cce.printStackTrace(); String s = "Cannot compile field!"; logger.fatal(s, cce); throw new RuntimeException(s, cce); } constructorBody += parameterName + "=$" + paramNumber + ";"; } try { CtField ctField = new CtField(pool .get(SbbLocalObjectInterceptor.class.getName()), "sbbLocalObjectInterceptor", concreteSbbLocalObject); concreteSbbLocalObject.addField(ctField); } catch (Exception cce) { String s = "Cannot add field!"; logger.fatal(s, cce); throw new RuntimeException(s, cce); } constructorBody += "this.sbbLocalObjectInterceptor = new " + SbbLocalObjectInterceptor.class.getName() + "( this ) ;"; } constructorBody += "}"; try { concreteSbbLocalObject.addConstructor(constructorWithParameter); constructorWithParameter.setBody(constructorBody); } catch (CannotCompileException e) { //Auto-generated catch block e.printStackTrace(); if (logger.isDebugEnabled()) { logger.debug("cannot compile constructor! body = " + constructorBody, e); } throw new RuntimeException("cannot compile constructor!", e); } if (logger.isDebugEnabled()) { logger.debug("ConstructorWithParameter created: " + constructorBody); } } private void generateEqualsMethod() { // Override the equals method in the genrated class. String methodToAdd = "public boolean equals(Object other) { " + "return this.getClass().equals(other.getClass() ) && " + "this.getSbbEntityId().equals(((" + SbbLocalObjectConcrete.class.getName() + ")other).getSbbEntityId());" + "}"; try { CtMethod methodTest = CtNewMethod.make(methodToAdd, concreteSbbLocalObject); concreteSbbLocalObject.addMethod(methodTest); } catch (CannotCompileException cce) { cce.printStackTrace(); logger.error("cannot generate method ", cce); throw new RuntimeException("error generating method ", cce); } } private void generateGetSbbEntityId() { String methodToAdd = "public String getSbbEntityId() { return this.getSbbEntity().getSbbEntityId(); }"; try { CtMethod methodTest = CtNewMethod.make(methodToAdd, concreteSbbLocalObject); concreteSbbLocalObject.addMethod(methodTest); } catch (CannotCompileException cce) { cce.printStackTrace(); logger.error("cannot generate method ", cce); throw new RuntimeException("error generating method ", cce); } } /** * Generates the concrete methods of the class * * @param interfaceMethods * the methods to implement coming from the * ActivityContextInterface developer * @param sbbAbstractClassName */ private void generateConcreteMethods(Map interfaceMethods, String sbbAbstractClassName) { if (interfaceMethods == null) return; Iterator it = interfaceMethods.values().iterator(); while (it.hasNext()) { CtMethod interfaceMethod = (CtMethod) it.next(); if (interfaceMethod == null) return; // The following methods are implemented in the superclass if (interfaceMethod.getName().equals("isIdentical") || interfaceMethod.getName().equals("getSbbPriority") || interfaceMethod.getName().equals("remove") || interfaceMethod.getName().equals("setSbbPriority")) continue; String methodToAdd = "public "; //Add the return type boolean hasReturn = false; CtClass returnType = null; try { // There's probably a more elegant way to do this. returnType = interfaceMethod.getReturnType(); if (returnType.equals(CtClass.voidType)) { methodToAdd += "void "; } else { methodToAdd = methodToAdd .concat(returnType.getName() + " "); hasReturn = true; } } catch (NotFoundException nfe) { nfe.printStackTrace(); methodToAdd = methodToAdd + "void "; } //Add the method name methodToAdd += interfaceMethod.getName() + "("; //Add the parameters CtClass[] parameterTypes = null; ; try { parameterTypes = interfaceMethod.getParameterTypes(); for (int argNumber = 0; argNumber < parameterTypes.length; argNumber++) { methodToAdd = methodToAdd.concat(parameterTypes[argNumber] .getName() + " arg_" + argNumber); if (argNumber + 1 < parameterTypes.length) methodToAdd = methodToAdd + ","; } } catch (NotFoundException nfe) { nfe.printStackTrace(); throw new RuntimeException("unexpected Exception ! ", nfe); } methodToAdd = methodToAdd + ") { "; // We need to do this in a type neutral way ! methodToAdd += "getSbbEntity().checkReEntrant();"; methodToAdd = methodToAdd + Object.class.getName() + " concrete = " + " getSbbEntity().getSbbObject().getSbbConcrete();"; //These methods are delegated to superclass. // Note the convoluted code here because finally is not supported // yet by javaassist. methodToAdd += "Object[] args = new Object [" + parameterTypes.length + "];"; methodToAdd += "Class[] types = new Class [" + parameterTypes.length + "];"; if (parameterTypes != null && parameterTypes.length > 0) { for (int argNumber = 0; argNumber < parameterTypes.length; argNumber++) { methodToAdd += "args[" + argNumber + "] = "; // Check if parameter type is primitive and add the wrapper // types. if (parameterTypes[argNumber].isPrimitive()) { CtClass ptype = parameterTypes[argNumber]; if (ptype.equals(CtClass.intType)) { methodToAdd += "new Integer (" + "arg_" + argNumber + ");"; } else if (ptype.equals(CtClass.booleanType)) { methodToAdd += "new Boolean (" + "arg_" + argNumber + ");"; } else if (ptype.equals(CtClass.longType)) { methodToAdd += "new Long (" + "arg_" + argNumber + ");"; } else if (ptype.equals(CtClass.shortType)) { methodToAdd += "new Short (" + "arg_" + argNumber + ");"; } else if (ptype.equals(CtClass.floatType)) { methodToAdd += "new Float (" + "arg_" + argNumber + ");"; } else if (ptype.equals(CtClass.doubleType)) { methodToAdd += "new Double (" + "arg_" + argNumber + ");"; } else if (ptype.equals(CtClass.charType)) { methodToAdd += "new Character(" + "arg_" + argNumber + ");"; } } else { methodToAdd += "arg_" + argNumber + ";"; } } for (int i = 0; i < parameterTypes.length; i++) { methodToAdd += "types[" + i + "] = "; if (parameterTypes[i].isPrimitive()) { CtClass ptype = parameterTypes[i]; if (ptype.equals(CtClass.intType)) { methodToAdd += "Integer.TYPE;"; } else if (ptype.equals(CtClass.booleanType)) { methodToAdd += "Boolean.TYPE;"; } else if (ptype.equals(CtClass.longType)) { methodToAdd += "Long.TYPE;"; } else if (ptype.equals(CtClass.shortType)) { methodToAdd += "Short.TYPE;"; } else if (ptype.equals(CtClass.floatType)) { methodToAdd += "Float.TYPE;"; } else if (ptype.equals(CtClass.doubleType)) { methodToAdd += "Double.TYPE;"; } else if (ptype.equals(CtClass.charType)) { methodToAdd += "Character.TYPE;"; } } else { methodToAdd += SleeContainerUtils.class.getName() + ".getCurrentThreadClassLoader().loadClass(" + parameterTypes[i].getName() + ".class.getName()); "; //.class // ; " // ; } } } if (hasReturn) { methodToAdd += " return " + "(" + returnType.getName() + ")"; } if (returnType.isPrimitive()) { methodToAdd += "sbbLocalObjectInterceptor.invokeAndReturn" + returnType.getSimpleName() + "(concrete," + "\"" + interfaceMethod.getName() + "\"" + ", args, types); "; } else { methodToAdd += "sbbLocalObjectInterceptor.invokeAndReturnObject(concrete," + "\"" + interfaceMethod.getName() + "\"" + ", args, types );"; } methodToAdd += "}"; //Add the implementation code if (logger.isDebugEnabled()) { logger.debug("Method " + methodToAdd + " added"); } CtMethod methodTest; try { methodTest = CtNewMethod.make(methodToAdd, concreteSbbLocalObject); concreteSbbLocalObject.addMethod(methodTest); } catch (CannotCompileException cce) { cce.printStackTrace(); throw new RuntimeException("error generating method ", cce); } } } }