package com.voxeo.tropo.app; import java.lang.reflect.Field; import java.util.Iterator; import java.util.Properties; import javax.script.CompiledScript; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptException; import javax.script.SimpleScriptContext; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.sip.ConvergedHttpSession; import javax.servlet.sip.SipApplicationSession; import javax.servlet.sip.SipFactory; import javax.servlet.sip.SipServletRequest; import javax.servlet.sip.SipSession; import org.apache.log4j.Logger; import com.voxeo.tropo.core.Call; import com.voxeo.tropo.core.CallImpl; import com.voxeo.tropo.core.IncomingCall; import com.voxeo.tropo.core.SimpleCallFactory; import com.voxeo.tropo.core.SimpleIncomingCall; import com.voxeo.tropo.util.Utils; import com.voxeo.sipmethod.mrcp.client.MrcpFactory; public class SimpleInstance extends AbstractInstance { private static final Logger LOG = Logger.getLogger(SimpleInstance.class); ScriptContext _context; CompiledScript _script; SipFactory _sipFactory; MrcpFactory _mrcpFactory; Application _app; static ThreadLocal<ApplicationInstance> _self = new ThreadLocal<ApplicationInstance>(); public SimpleInstance(final SipServletRequest invite, final CompiledScript script, final Application app) { this(invite.getSession().getApplicationSession(), invite, script, app); invite.getSession().getApplicationSession().setInvalidateWhenReady(false); } public SimpleInstance(final HttpServletRequest invite, final CompiledScript script, final Application app) { this(((ConvergedHttpSession) invite.getSession()).getApplicationSession(), invite, script, app); } public SimpleInstance(final SipApplicationSession appSession, final ServletRequest invite, final CompiledScript script, final Application app) { super(appSession, invite, app); _script = script; _session.setAttribute(ApplicationInstance.INST, this); _sipFactory = app.getManager().getSipFactory(); _mrcpFactory = app.getManager().getMrcpFactory(); _app = app; _context = new SimpleScriptContext(); // should already been set setLogContext(); LOG.info(toString() + " has been created."); // set a default null string to facilitate the judge in script layer _context.setAttribute("incomingCall", "nullCall", ScriptContext.ENGINE_SCOPE); // expose application instance to the script _context.setAttribute("appInstance", this, ScriptContext.ENGINE_SCOPE); final Properties params = _app.getParameters(); if (params != null) { for (final Object name : params.keySet()) { _context.setAttribute((String) name, params.get(name), ScriptContext.ENGINE_SCOPE); if (LOG.isDebugEnabled()) { LOG.debug(name + ":" + params.get(name) + " is added into the context of app instance : " + this.toString()); } } } final SimpleCallFactory cf = new SimpleCallFactory(this); _context.setAttribute("callFactory", cf, ScriptContext.ENGINE_SCOPE); if (LOG.isDebugEnabled()) { LOG.debug(cf.toString() + " is added into the context of app instance : " + this.toString()); } } public static ApplicationInstance getCurrentApplicationInstance() { return _self.get(); } Field getField(Object o, String name) throws SecurityException { try { return o.getClass().getDeclaredField(name); } catch (NoSuchFieldException e) { ; //ignore } return null; } /** * return old ScriptEngine in the CompiledScripte * * @param cs * @param eng * @return */ ScriptEngine replaceScriptEgnine(CompiledScript cs, ScriptEngine eng) { if (cs instanceof SimulatedCompiledScript) { return eng; } String fn = "engine"; ScriptEngine oldEng = cs.getEngine(); try { Field f = getField(cs, fn); // groovy, js if (f == null) { fn = "_engine"; f = getField(cs, fn); // php } if (f == null) { fn = "this$0"; f = getField(cs, fn); // jython, jruby } if (f == null) { Field[] fs = cs.getClass().getDeclaredFields(); StringBuffer buf = new StringBuffer("All available fields in " + cs.getClass().getName() + " are: "); for (int i = 0; i < fs.length; i++) { if (i == fs.length - 1) { buf.append('[').append(fs[i].getName()).append(']'); } else { buf.append('[').append(fs[i].getName()).append(']').append(","); } } LOG.error(toString() + " no field with name 'engine' or '_engine' . " + buf.toString()); return eng; } final boolean accessibility = f.isAccessible(); f.setAccessible(true); f.set(cs, eng); f.setAccessible(accessibility); if (LOG.isDebugEnabled()) { LOG.debug(toString() + " replaced script engine [" + oldEng + "] --> [" + cs.getEngine() + "] in field <" + fn + ">"); } return oldEng; } catch (SecurityException e) { if (LOG.isDebugEnabled()) { LOG.debug(toString() + " error replacing script engine : " + e.getMessage(), e); } } catch (IllegalArgumentException e) { if (LOG.isDebugEnabled()) { LOG.debug(toString() + " error replacing script engine : " + e.getMessage(), e); } } catch (IllegalAccessException e) { if (LOG.isDebugEnabled()) { LOG.debug(toString() + " error replacing script engine : " + e.getMessage(), e); } } return eng; } public void run() { setLogContext(); _self.set(this); IncomingCall call = null; SipApplicationSession appSession = getApplicationSession(); if (_invite instanceof SipServletRequest) { final SimpleCallFactory cf = (SimpleCallFactory) _context.getAttribute("callFactory"); call = new SimpleIncomingCall(cf, (SipServletRequest) _invite, this); _context.setAttribute("incomingCall", call, ScriptContext.ENGINE_SCOPE); if (LOG.isDebugEnabled()) { LOG.debug(call.toString() + " is added into the context"); } } ScriptEngine eng = null; ScriptEngine oldEng = null; try { LOG.info(this + " starts execution."); if (_script instanceof SimulatedCompiledScript) { _script.eval(_context); } else { eng = ((LocalApplicationManager) getApp().getManager()).getScriptEngine(getApp().getType()); oldEng = replaceScriptEgnine(_script, eng); _script.eval(_context); } LOG.info(this + " ends execution."); } catch (final ScriptException e) { LOG.error(Utils.buildScriptExceptionMessage(this, "runtime", e), e); } catch (final SecurityException e) { LOG.error(this + " violates the sandbox: " + e.getMessage(), e); } catch (final Throwable t) { LOG.error(this + " has unknown errors: " + t.getMessage(), t); } finally { if (eng != null) { if (oldEng != null && eng != oldEng) { replaceScriptEgnine(_script, oldEng); } ((LocalApplicationManager) getApp().getManager()).putScriptEngine(getApp().getType(), eng); } if (appSession != null && appSession.isValid()) { try { final Iterator<?> sessions = appSession.getSessions("SIP"); while (sessions.hasNext()) { try { final SipSession session = (SipSession) sessions.next(); if (LOG.isDebugEnabled()) { LOG.debug(appSession + " has " + session); } if (session != null && session.isValid()) { final Call c = (Call) session.getAttribute(CallImpl.INST); if (c != null) { c.hangup(); } } } catch (final Throwable t) { ; } } } catch (final Throwable t) { ; } if (appSession.isReadyToInvalidate()) { appSession.invalidate(); } else { appSession.setInvalidateWhenReady(true); } } System.gc(); } } public void terminate() { } }