package org.mobicents.slee.container.deployment.profile; import java.beans.Introspector; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.Modifier; import javassist.NotFoundException; import javax.slee.SLEEException; import javax.slee.management.DeploymentException; import javax.slee.profile.ProfileTable; import org.apache.log4j.Logger; import org.mobicents.slee.container.component.ProfileSpecificationComponent; import org.mobicents.slee.container.component.deployment.ClassPool; import org.mobicents.slee.container.component.deployment.jaxb.descriptors.ProfileSpecificationDescriptorImpl; import org.mobicents.slee.container.deployment.ClassUtils; import org.mobicents.slee.container.deployment.ConcreteClassGeneratorUtils; import org.mobicents.slee.container.profile.ProfileQueryHandler; import org.mobicents.slee.container.profile.ProfileTableImpl; /** * * ConcreteProfileTableGenerator.java * * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a> * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a> */ public class ConcreteProfileTableGenerator { private static final Logger logger = Logger.getLogger(ConcreteProfileTableGenerator.class); public static final String _INTERCEPTOR_QUERY = ProfileQueryHandler.class.getName() + ".handle"; public static final String _QUERY_METHOD_NAME_PREFIX = "query"; private ProfileSpecificationComponent component = null; private String cmpProfileInterfaceName = null; private String profileTableInterfaceName = null; private String profileTableConcreteClassName = null; private ClassPool pool; private CtClass profileTableInterface = null; private CtClass sleeProfileTableInterface = null; private CtClass mobicentsConcreteProfileTableClass = null; private CtClass profileTableConcreteClass = null; public ConcreteProfileTableGenerator(ProfileSpecificationComponent component) { super(); this.component = component; ProfileSpecificationDescriptorImpl descriptor = component.getDescriptor(); cmpProfileInterfaceName = descriptor.getProfileClasses().getProfileCMPInterface().getProfileCmpInterfaceName(); profileTableInterfaceName = descriptor.getProfileClasses().getProfileTableInterface() == null ? null : descriptor.getProfileClasses().getProfileTableInterface().getProfileTableInterfaceName(); pool = component.getClassPool(); } public void generateProfileTable() throws Exception { try { mobicentsConcreteProfileTableClass = pool.get(ProfileTableImpl.class.getName()); } catch (NotFoundException nfe) { throw new SLEEException("Failed to locate ProfileTableConcreteImpl class for " + component, nfe); } try { sleeProfileTableInterface = pool.get(ProfileTable.class.getName()); } catch (NotFoundException nfe) { throw new SLEEException("Failed to locate ProfileTable interface for " + component, nfe); } //Those methods are already implemented. Map<String,CtMethod> alreadyImplemented = ClassUtils.getInterfaceMethodsFromInterface(sleeProfileTableInterface); Map<String, CtMethod> queriesMapToDivert = new HashMap<String, CtMethod>(); CtClass[] targetInterfaces = null; if (profileTableInterfaceName != null) { // we have some interface, also we have methods from generic ProfileTable interface, we want only queries. try { profileTableInterface = pool.get(profileTableInterfaceName); queriesMapToDivert = ClassUtils.getInterfaceMethodsFromInterface(profileTableInterface); //Lets remove methods that we already have queriesMapToDivert.keySet().removeAll(alreadyImplemented.keySet()); } catch (NotFoundException nfe) { throw new DeploymentException("Failed to locate ProfiteTable interface for " + component, nfe); } profileTableConcreteClassName = ConcreteClassGeneratorUtils.PROFILE_TABLE_CLASS_NAME_PREFIX + profileTableInterface.getName() + ConcreteClassGeneratorUtils.PROFILE_TABLE_CLASS_NAME_SUFFIX; CtClass[] presentInterfaces = this.mobicentsConcreteProfileTableClass.getInterfaces(); targetInterfaces = new CtClass[presentInterfaces.length + 1]; for (int index = 0; index < presentInterfaces.length; index++) { targetInterfaces[index] = presentInterfaces[index]; } targetInterfaces[targetInterfaces.length - 1] = profileTableInterface; } else { //There is no custom profile table interface. profileTableConcreteClassName = ConcreteClassGeneratorUtils.PROFILE_TABLE_CLASS_NAME_PREFIX + cmpProfileInterfaceName + ConcreteClassGeneratorUtils.PROFILE_TABLE_CLASS_NAME_SUFFIX; targetInterfaces = this.mobicentsConcreteProfileTableClass.getInterfaces(); } //lets make concrete class try { profileTableConcreteClass = pool.makeClass(profileTableConcreteClassName); } catch (Exception e) { throw new SLEEException("Failed to create ProfileTableConcreteClass implementation class."); } ConcreteClassGeneratorUtils.createInterfaceLinks(profileTableConcreteClass, targetInterfaces); ConcreteClassGeneratorUtils.createInheritanceLink(profileTableConcreteClass, mobicentsConcreteProfileTableClass); //now we have to instrument queries methods. generateQueries(profileTableConcreteClass,queriesMapToDivert); //write and store try { // @@2.4 + -> 3.4 + profileTableConcreteClass.writeFile(component.getDeploymentDir().getAbsolutePath()); if (logger.isDebugEnabled()) { logger.debug("Concrete Class " + profileTableConcreteClass.getName() + " generated in the following path " + component.getDeploymentDir().getAbsolutePath()); } } catch (Exception e) { throw new SLEEException("Unexpected exception generating ProfileTableConcrete Class.", e); } finally { // let go, so that it's not holding subsequent deployments of the same profile component. // This would not have been necessary is the ClassPool is not one shared instance in the SLEE, // but there is instead a hierarchy mimicing the classloader hierarchy. This also makes // our deployer essentially single threaded. profileTableConcreteClass.defrost(); } try { // load the generated class Class clazz = component.getClassLoader().loadClass(profileTableConcreteClass.getName()); component.setProfileTableConcreteClass(clazz); } catch (ClassNotFoundException cnfe) { throw new SLEEException("Unexpected exception generating ProfileTableConcrete Class.", cnfe); } } private void generateQueries(CtClass profileTableConcreteClass, Map<String, CtMethod> queriesMapToDivert) throws Exception { Iterator<Map.Entry<String, CtMethod>> it = queriesMapToDivert.entrySet().iterator(); while(it.hasNext()) { instrumentQuery(profileTableConcreteClass,it.next().getValue(),_INTERCEPTOR_QUERY); it.remove(); } } private void instrumentQuery(CtClass profileTableConcreteClass, CtMethod iMethod, String interceptorQuery) throws Exception { if(logger.isDebugEnabled()) { logger.debug("About to instrument query method: " + iMethod.getName() + ", into: " + profileTableConcreteClass); } CtMethod method = CtNewMethod.copy(iMethod, profileTableConcreteClass, null); method.setModifiers(method.getModifiers() & ~Modifier.ABSTRACT); String queryName = method.getName(); if(queryName.startsWith(_QUERY_METHOD_NAME_PREFIX)) { queryName= Introspector.decapitalize(queryName.replace(_QUERY_METHOD_NAME_PREFIX, "")); } else { throw new SLEEException("Method has wrong prefix, method name: " + queryName); } String body = "{ return " + _INTERCEPTOR_QUERY + "(this,\"" + queryName + "\",$args); }"; if(logger.isDebugEnabled()) { logger.debug("Instrument query method: " + method.getName() + ", into: " + profileTableConcreteClass + ", with body:\n" + body); } method.setBody(body); profileTableConcreteClass.addMethod(method); } }