/*
* TclHandler.java
*
* Brazil project web application Framework,
* export version: 1.1
* Copyright (c) 1999-2000 Sun Microsystems, Inc.
*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is included as the file "license.terms",
* and also available at http://www.sun.com/
*
* The Original Code is from:
* Brazil project web application Framework release 1.1.
* The Initial Developer of the Original Code is: suhler.
* Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): cstevens, suhler.
*
* Version: 1.16
* Created by suhler on 99/04/04
* Last modified by suhler on 00/12/11 13:33:06
*/
package sunlabs.brazil.tcl;
import sunlabs.brazil.server.FileHandler;
import sunlabs.brazil.server.Handler;
import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.session.SessionManager;
import tcl.lang.Interp;
import tcl.lang.ReflectObject;
import tcl.lang.TCL;
import tcl.lang.TclException;
import tcl.lang.TclUtil;
import java.io.File;
import java.io.IOException;
/**
* Handler for writing handlers in tcl.
* Anytime a request is made, call the <b>respond</b> callback
* in the tcl code, which is provided with the
* {@link sunlabs.brazil.server.Request} object as an argument.
* <p>
* This provides a bare-bones tcl interface. The startup script, which is
* sourced once upon startup,
* should provide a friendlier interface.
* <p>
* One Tcl interpreter is started for each session.
* The SessionID property of the request is used to choose the session.
* if no Session ID is available, a single interpreter is used for each
* request.
* The interpreter is Initialized the first time it is referenced for a
* session, or if the "SessionID" variable is NOT set.
* <p>
* This handler requires that <code>tcljava.jar</code> and
* <code>jacl.jar</code> jar files (both available
* <a href="http://www.scriptics.com/software/java1.0.html">)here</a>.
* <p>
* The following server properties are used:
* <dl class=props>
* <dt>callback <dd>The name of the TCL script to call at each request.
* Defaults to <code>respond</code>.
* <dt>prefix <dd> The URL prefix to match (defaults to "/").
* <dt>script <dd> The name of the TCL file sourced on startup.
* The {@link #init} parameters a make available as the global
* variables <code>prefix</code> and <code>server</code>.
* <dt>suffix <dd> The URL suffix to match, if any.
* </dl>
*
* @author Stephen Uhler
* @version 1.16, 00/12/11
*/
public class TclHandler implements Handler {
Server server;
String propsPrefix;
String scriptName;
String urlPrefix;
String urlSuffix;
String callback;
static final String SCRIPT = "script";
static final String PREFIX = "prefix";
static final String SUFFIX = "suffix";
static final String CALLBACK = "callback";
/**
* Create a tcl interp, extract the properties, and run the init script
*/
public boolean
init(Server server, String prefix) {
this.server = server;
propsPrefix = prefix;
scriptName = server.props.getProperty(prefix + SCRIPT,prefix + "tcl");
urlPrefix = server.props.getProperty(prefix + PREFIX,"/");
urlSuffix = server.props.getProperty(prefix + SUFFIX,"");
callback = server.props.getProperty(prefix + CALLBACK,"respond");
File scriptFile = new File(scriptName);
if (!scriptFile.isAbsolute()) {
scriptFile = new File(server.props.getProperty(FileHandler.ROOT,"."), scriptName);
}
scriptName = scriptFile.getAbsolutePath();
server.log(Server.LOG_DIAGNOSTIC, prefix, "Using: " + scriptName);
return true;
}
/*
* Find (or create) the interpreter.
* Call the tcl callback script.
* The <code>request</code> object reference is appended to the
* <code>callback</code> parameter.
* @return true, if the callback script returns "true" or "1".
*/
public boolean
respond(Request request) throws IOException {
if (!request.url.startsWith(urlPrefix)) {
return false;
}
if (!request.url.endsWith(urlSuffix)) {
return false;
}
/*
* Find the proper interp, creating it if needed. If newly created
* (or if no SessionID variable was found) - initialize the interp
*/
// request.props.list(System.out);
String sessionId = request.props.getProperty("SessionID","common");
request.log(Server.LOG_DIAGNOSTIC, " Using session: " + sessionId);
Interp interp = (Interp) SessionManager.getSession(sessionId, "TCL",
Interp.class);
setupInterp(interp, sessionId);
int code = 0;
String result = interp.getResult().toString();
synchronized (interp) {
try {
interp.eval(callback + " " +
ReflectObject.newInstance(interp, Request.class, request));
} catch (TclException e) {
code = e.getCompletionCode();
String trace = e.toString();
System.out.println("Tcl Oops: " + code + " " + e);
if (code == 1) {
try {
trace = interp.getVar("errorInfo", TCL.GLOBAL_ONLY).toString();
} catch (Exception e1) {}
}
request.log(Server.LOG_WARNING, propsPrefix + trace);
}
result = interp.getResult().toString();
} // end sync block
if ((code==0 || code==2) && (result.equalsIgnoreCase("true") ||
result.equals("1"))) {
return true;
} else {
return false;
}
}
/**
* Setup a tcl interpreter for this session
*/
private void
setupInterp(Interp newInterp, String id) {
try {
newInterp.getVar("SessionID", TCL.GLOBAL_ONLY);
return;
} catch (TclException e) {
System.out.println("New interp: " + e);
}
try {
TclUtil.setVar(newInterp, "tcl_interactive", "0", TCL.GLOBAL_ONLY);
TclUtil.setVar(newInterp, "argv0", scriptName, TCL.GLOBAL_ONLY);
TclUtil.setVar(newInterp, "prefix", propsPrefix, TCL.GLOBAL_ONLY);
TclUtil.setVar(newInterp, "SessionID", id, TCL.GLOBAL_ONLY);
TclUtil.setVar(newInterp, "server",
ReflectObject.newInstance(newInterp, Server.class, server),
TCL.GLOBAL_ONLY);
newInterp.evalFile(scriptName);
} catch (TclException e) {
int code = e.getCompletionCode();
String trace = e.toString();
if (code == 1) {
try {
trace = newInterp.getVar("errorInfo",
TCL.GLOBAL_ONLY).toString();
} catch (Exception e1) {}
}
server.log(Server.LOG_WARNING, null, trace);
}
return;
}
}
|