001: /*
002: * TclHandler.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 1999-2000 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: suhler.
018: * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, suhler.
022: *
023: * Version: 1.16
024: * Created by suhler on 99/04/04
025: * Last modified by suhler on 00/12/11 13:33:06
026: */
027:
028: package sunlabs.brazil.tcl;
029:
030: import sunlabs.brazil.server.FileHandler;
031: import sunlabs.brazil.server.Handler;
032: import sunlabs.brazil.server.Request;
033: import sunlabs.brazil.server.Server;
034: import sunlabs.brazil.session.SessionManager;
035:
036: import tcl.lang.Interp;
037: import tcl.lang.ReflectObject;
038: import tcl.lang.TCL;
039: import tcl.lang.TclException;
040: import tcl.lang.TclUtil;
041:
042: import java.io.File;
043: import java.io.IOException;
044:
045: /**
046: * Handler for writing handlers in tcl.
047: * Anytime a request is made, call the <b>respond</b> callback
048: * in the tcl code, which is provided with the
049: * {@link sunlabs.brazil.server.Request} object as an argument.
050: * <p>
051: * This provides a bare-bones tcl interface. The startup script, which is
052: * sourced once upon startup,
053: * should provide a friendlier interface.
054: * <p>
055: * One Tcl interpreter is started for each session.
056: * The SessionID property of the request is used to choose the session.
057: * if no Session ID is available, a single interpreter is used for each
058: * request.
059: * The interpreter is Initialized the first time it is referenced for a
060: * session, or if the "SessionID" variable is NOT set.
061: * <p>
062: * This handler requires that <code>tcljava.jar</code> and
063: * <code>jacl.jar</code> jar files (both available
064: * <a href="http://www.scriptics.com/software/java1.0.html">)here</a>.
065: * <p>
066: * The following server properties are used:
067: * <dl class=props>
068: * <dt>callback <dd>The name of the TCL script to call at each request.
069: * Defaults to <code>respond</code>.
070: * <dt>prefix <dd> The URL prefix to match (defaults to "/").
071: * <dt>script <dd> The name of the TCL file sourced on startup.
072: * The {@link #init} parameters a make available as the global
073: * variables <code>prefix</code> and <code>server</code>.
074: * <dt>suffix <dd> The URL suffix to match, if any.
075: * </dl>
076: *
077: * @author Stephen Uhler
078: * @version 1.16, 00/12/11
079: */
080:
081: public class TclHandler implements Handler {
082: Server server;
083: String propsPrefix;
084: String scriptName;
085: String urlPrefix;
086: String urlSuffix;
087: String callback;
088:
089: static final String SCRIPT = "script";
090: static final String PREFIX = "prefix";
091: static final String SUFFIX = "suffix";
092: static final String CALLBACK = "callback";
093:
094: /**
095: * Create a tcl interp, extract the properties, and run the init script
096: */
097:
098: public boolean init(Server server, String prefix) {
099: this .server = server;
100: propsPrefix = prefix;
101: scriptName = server.props.getProperty(prefix + SCRIPT, prefix
102: + "tcl");
103: urlPrefix = server.props.getProperty(prefix + PREFIX, "/");
104: urlSuffix = server.props.getProperty(prefix + SUFFIX, "");
105: callback = server.props.getProperty(prefix + CALLBACK,
106: "respond");
107: File scriptFile = new File(scriptName);
108: if (!scriptFile.isAbsolute()) {
109: scriptFile = new File(server.props.getProperty(
110: FileHandler.ROOT, "."), scriptName);
111: }
112: scriptName = scriptFile.getAbsolutePath();
113: server.log(Server.LOG_DIAGNOSTIC, prefix, "Using: "
114: + scriptName);
115: return true;
116: }
117:
118: /*
119: * Find (or create) the interpreter.
120: * Call the tcl callback script.
121: * The <code>request</code> object reference is appended to the
122: * <code>callback</code> parameter.
123: * @return true, if the callback script returns "true" or "1".
124: */
125:
126: public boolean respond(Request request) throws IOException {
127:
128: if (!request.url.startsWith(urlPrefix)) {
129: return false;
130: }
131: if (!request.url.endsWith(urlSuffix)) {
132: return false;
133: }
134:
135: /*
136: * Find the proper interp, creating it if needed. If newly created
137: * (or if no SessionID variable was found) - initialize the interp
138: */
139:
140: // request.props.list(System.out);
141: String sessionId = request.props.getProperty("SessionID",
142: "common");
143: request.log(Server.LOG_DIAGNOSTIC, " Using session: "
144: + sessionId);
145: Interp interp = (Interp) SessionManager.getSession(sessionId,
146: "TCL", Interp.class);
147: setupInterp(interp, sessionId);
148:
149: int code = 0;
150: String result = interp.getResult().toString();
151: synchronized (interp) {
152: try {
153: interp.eval(callback
154: + " "
155: + ReflectObject.newInstance(interp,
156: Request.class, request));
157: } catch (TclException e) {
158: code = e.getCompletionCode();
159: String trace = e.toString();
160: System.out.println("Tcl Oops: " + code + " " + e);
161: if (code == 1) {
162: try {
163: trace = interp.getVar("errorInfo",
164: TCL.GLOBAL_ONLY).toString();
165: } catch (Exception e1) {
166: }
167: }
168: request.log(Server.LOG_WARNING, propsPrefix + trace);
169: }
170: result = interp.getResult().toString();
171: } // end sync block
172: if ((code == 0 || code == 2)
173: && (result.equalsIgnoreCase("true") || result
174: .equals("1"))) {
175: return true;
176: } else {
177: return false;
178: }
179: }
180:
181: /**
182: * Setup a tcl interpreter for this session
183: */
184:
185: private void setupInterp(Interp newInterp, String id) {
186: try {
187: newInterp.getVar("SessionID", TCL.GLOBAL_ONLY);
188: return;
189: } catch (TclException e) {
190: System.out.println("New interp: " + e);
191: }
192: try {
193: TclUtil.setVar(newInterp, "tcl_interactive", "0",
194: TCL.GLOBAL_ONLY);
195: TclUtil.setVar(newInterp, "argv0", scriptName,
196: TCL.GLOBAL_ONLY);
197: TclUtil.setVar(newInterp, "prefix", propsPrefix,
198: TCL.GLOBAL_ONLY);
199: TclUtil.setVar(newInterp, "SessionID", id, TCL.GLOBAL_ONLY);
200: TclUtil.setVar(newInterp, "server", ReflectObject
201: .newInstance(newInterp, Server.class, server),
202: TCL.GLOBAL_ONLY);
203: newInterp.evalFile(scriptName);
204: } catch (TclException e) {
205: int code = e.getCompletionCode();
206: String trace = e.toString();
207: if (code == 1) {
208: try {
209: trace = newInterp.getVar("errorInfo",
210: TCL.GLOBAL_ONLY).toString();
211: } catch (Exception e1) {
212: }
213: }
214: server.log(Server.LOG_WARNING, null, trace);
215: }
216: return;
217: }
218: }
|