/***************************************************
* *
* Mobicents: The Open Source JSLEE Platform *
* *
* Distributable under LGPL license. *
* See terms of license at gnu.org. *
* *
***************************************************/
package org.mobicents.slee.container.component.deployment;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import javax.slee.SLEEException;
import javax.slee.management.DeploymentException;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.component.SbbComponent;
import org.mobicents.slee.container.component.deployment.ClassPool;
/**
* Class decorating the sbb abstract class. The byte code is modified IF
* necessary to trap SBB code that violates the SLEE spec:
* <pre>
* 1) write access to java:comp/env
* 2) new Thread()
* </pre>
* TODO: Currently we check the SBB abstract class itself, but not its
* ascendands or utility classes it uses.
*
* @author Ivelin Ivanov
*
*/
public class SbbAbstractClassDecorator {
/**
* the sbb abstract class
*/
private CtClass sbbAbstractClass = null;
/**
* Logger to logg information
*/
private static final Logger logger = Logger.getLogger(SbbAbstractClassDecorator.class);
/**
* the sbb concrete methods written by the SBB developer
*/
private Map concreteMethods = null;
private final SbbComponent component;
/**
* Optimization variable. Helps avoid writing to disk abstract classes, which are not modified.
*/
private boolean isAbstractSbbClassDecorated = false;
/**
* Constructor
*/
public SbbAbstractClassDecorator(SbbComponent component) {
this.component = component;
}
/**
* Decorate the abstract sbb Class
*
* @return the decorated sbb abstract class
*/
public boolean decorateAbstractSbb() throws DeploymentException {
ClassPool pool = component.getClassPool();
String sbbAbstractClassName = component.getDescriptor().getSbbAbstractClass().getSbbAbstractClassName();
try {
sbbAbstractClass = pool.get(sbbAbstractClassName);
} catch (NotFoundException nfe) {
throw new DeploymentException("Could not find Abstract Sbb Class: "
+ sbbAbstractClassName, nfe);
}
// populate the list of concrete methods. It will be needed by the
// decorating methods.
concreteMethods = new HashMap();
CtMethod[] methods = sbbAbstractClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
int mods = methods[i].getModifiers();
if (!Modifier.isAbstract(mods) && !Modifier.isNative(mods)) {
concreteMethods.put(methods[i].getName() + methods[i].getSignature(), methods[i]);
}
}
decorateENCBindCalls();
decorateNewThreadCalls();
if (isAbstractSbbClassDecorated) {
try {
String deployDir = component.getDeploymentDir().getAbsolutePath();
sbbAbstractClass.writeFile(deployDir);
sbbAbstractClass.detach();
// the file on disk is now in sync with the latest in-memory version
if (logger.isDebugEnabled()) {
logger.debug("Modified Abstract Class "
+ sbbAbstractClass.getName()
+ " generated in the following path "
+ deployDir);
}
//} catch (NotFoundException e) {
// String s = "Error writing modified abstract sbb class";
// logger.error(s,e);
// throw new DeploymentException (s,e);
} catch (Throwable e) {
throw new SLEEException ( e.getMessage(), e);
} finally {
sbbAbstractClass.defrost();
}
return true;
}
else {
return false;
}
}
/**
*
* If there are new Thread() calls, replace with exception.
*
* Required by SLEE 1.0 spec.
*
*
*/
private void decorateNewThreadCalls() {
// TODO: Find which TCK test shows the required functionality.
}
/**
*
* If there are bind calls to ENC, replace with
* OperationNotSupportedException exception.
*
* Required by SLEE 1.0 spec.
*
* @see com.opencloud.sleetck.lib.testsuite.sbb.abstractclass.Test522Test
*
*/
private void decorateENCBindCalls() {
Iterator iter = concreteMethods.values().iterator();
while (iter.hasNext()) {
CtMethod method = (CtMethod) iter.next();
try {
trapMethodIfENCBindCall(method);
} catch (CannotCompileException e) {
throw new SLEEException(
"Could not decorate ENC bind calls for sbb abstract class: "
+ sbbAbstractClass, e);
}
}
}
private void trapMethodIfENCBindCall(CtMethod method)
throws CannotCompileException {
method.instrument(new ExprEditor() {
public void edit(MethodCall m) throws CannotCompileException {
// the call has to be on a JNDI Context object
boolean isJndiCtx = false;
try {
// see if the method call is on javax.naming.Context or a derived type
if (m.getClassName().equals(javax.naming.Context.class.getName())) {
isJndiCtx = true;
} else {
CtClass cl0 = component.getClassPool().get(m.getClassName());
CtClass[] cl0Interfaces = cl0.getInterfaces();
for (int i = 0; i < cl0Interfaces.length; i++) {
CtClass cl0If = cl0Interfaces[i];
if (cl0If.getName().equals(javax.naming.Context.class.getName())) {
isJndiCtx = true;
break;
}
}
}
} catch (NotFoundException e) {
// The class is not in the pool so ignore it
return;
//throw new CannotCompileException(e);
}
if (!isJndiCtx)
return;
/*
* trap calls to Contenxt bind, unbind, rebind
*/
if (m.getMethodName().endsWith("bind") || m.getMethodName().equals("createSubcontext")) {
m.replace("{ "
+ "String fullJndiName = $0.composeName($1, $0.getNameInNamespace());"
+ "System.err.println(\"TRAPPED SBB METHOD - JNDI BIND CALL; fullJndiName: \" + fullJndiName);"
+ "if (fullJndiName.startsWith(\"java:comp/env\")"
+ " || " + "fullJndiName.startsWith(\"env\")"
+ ") {"
+ " throw new javax.naming.OperationNotSupportedException(\"SBB is not allowed write access to java:comp/env. See SLEE 1.0 spec for details. \"); "
+ "} "
+ "else {$_ = $proceed($$);}; "
+ "}");
isAbstractSbbClassDecorated = true;
}
}
});
} // trapMethodIfENCBindCall
}