sunlabs.brazil.tcl.TclServerTemplate Maven / Gradle / Ivy
Show all versions of sunlabs.brazil Show documentation
/*
* TclServerTemplate.java
*
* Brazil project web application toolkit,
* export version: 2.3
* Copyright (c) 1999-2004 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 toolkit release 2.3.
* The Initial Developer of the Original Code is: cstevens.
* Portions created by cstevens are Copyright (C) Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): cstevens, suhler.
*
* Version: 2.4
* Created by cstevens on 99/10/19
* Last modified by suhler on 04/11/30 15:11:27
*
* Version Histories:
*
* 2.4 04/11/30-15:11:27 (suhler)
* fixed sccs version string
*
* 2.3 03/08/01-16:19:31 (suhler)
* fixes for javadoc
*
* 2.2 03/07/07-14:45:22 (suhler)
* Merged changes between child workspace "/home/suhler/brazil/naws" and
* parent workspace "/net/mack.eng/export/ws/brazil/naws".
*
* 1.20.1.1 03/07/07-14:08:50 (suhler)
* use addClosingTag() convenience method
*
* 2.1 02/10/01-16:37:25 (suhler)
* version change
*
* 1.20 01/08/14-16:40:10 (suhler)
* add "eval" option for consistency with python
*
* 1.19 01/03/12-17:42:35 (cstevens)
* Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and
* parent workspace "/export/ws/brazil/naws".
*
* 1.18 01/03/06-09:10:40 (suhler)
* move interp creation to first use, to avoid redundant interp creation.
* Allow ...
*
* 1.17.1.1 01/02/12-17:15:17 (cstevens)
* debugging
*
* 1.17 00/12/11-13:33:12 (suhler)
* add class=props for automatic property extraction
*
* 1.16 00/11/15-09:49:29 (suhler)
* add the session id into the tcl namespace
*
* 1.15 00/10/05-15:52:07 (cstevens)
* lint
*
* 1.14 00/07/06-16:00:14 (suhler)
* doc updates
*
* 1.13 00/07/05-14:14:03 (cstevens)
* TclServerTemplate can load init script from resource fork or file system.
* TclServerTemplate passes "server" and "prefix" to init script.
*
* 1.12 00/06/30-10:31:32 (cstevens)
* Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and
* parent workspace "/export/ws/brazil/naws".
*
* 1.10.1.1 00/06/30-10:29:58 (cstevens)
* request reused too much
*
* 1.11 00/06/29-10:47:32 (suhler)
* fixed single character typo
* .
*
* 1.10 00/06/20-07:38:48 (suhler)
* expose handler prefix, and prefix and request to init script
*
* 1.9 00/05/31-13:52:23 (suhler)
* name change
*
* 1.8 00/04/20-13:11:20 (cstevens)
* Automatically "package require java" into new interps
*
* 1.7 00/04/12-16:05:41 (cstevens)
* If debug turned on, put errorInfo in an HTML comment if error occurred in
* script.
*
* 1.6 00/02/11-08:58:19 (suhler)
* better memory management
*
* 1.5 99/10/27-13:21:32 (suhler)
* Merged changes between child workspace "/home/suhler/brazil/naws" and
* parent workspace "/net/mack.eng/export/ws/brazil/naws".
*
* 1.4 99/10/26-18:55:22 (cstevens)
* Changed TclServerTemplate and BSLTemplate to use the config parameter "debug"
* to decide whether to emit comments into the resultant HTML file.
*
* 1.3.1.1 99/10/22-09:59:51 (suhler)
* Merged changes between child workspace "/home/suhler/brazil/naws" and
* parent workspace "/net/mack.eng/export/ws/brazil/naws".
*
* 1.2.1.1 99/10/22-09:57:04 (suhler)
* ???
*
* 1.3 99/10/21-18:27:07 (cstevens)
* Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and
* parent workspace "/net/mack/export/ws/brazil/naws".
*
* 1.1.1.1 99/10/21-18:23:57 (cstevens)
* TclServerTemplate: added configuration property to source an initialization
* script into the Tcl Interp the first time the Interp is used.
*
* 1.2 99/10/20-15:08:49 (suhler)
* fixed imports
*
* 1.2 99/10/19-19:00:57 (Codemgr)
* SunPro Code Manager data about conflicts, renames, etc...
* Name history : 2 1 tcl/TclServerTemplate.java
* Name history : 1 0 tclHandlers/TclServerTemplate.java
*
* 1.1 99/10/19-19:00:56 (cstevens)
* date and time created 99/10/19 19:00:56 by cstevens
*
*/
package sunlabs.brazil.tcl;
import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.handler.ResourceHandler;
import sunlabs.brazil.template.RewriteContext;
import sunlabs.brazil.template.Template;
import sunlabs.brazil.util.Format;
import tcl.lang.Interp;
import tcl.lang.ReflectObject;
import tcl.lang.TCL;
import tcl.lang.TclException;
import tcl.lang.TclObject;
import tcl.lang.TclTemplateChannel;
import tcl.lang.TclUtil;
import java.io.IOException;
import java.util.Properties;
/**
* The TclServerTemplate
looks for each
* <server language="tcl">
* (or <"tcl">
)
* tag in an HTML page and treats the following data up to the next
* </server>
tag as a Tcl script to evaluate.
* If the optional attribute eval
is present,
* the all ${...} constructs are replaced using
* {@link sunlabs.brazil.util.Format#subst}
* before being passed to the Tcl interpreter.
*
* The reason that Tcl scripts are included in an HTML page is usually
* to generate dynamic, server-side content. After running this template,
* everything between and including the <server>
and
* </server>
tags is replaced with the result of
* evaluating the Tcl script as follows:
* - Anything printed to the standard output of the Tcl interpreter is
* added to the HTML document (for instance,
puts "hello"
).
* - Additionally, if the Tcl script returns a value, that value is
* added to the HTML document (for instance,
return "bob"
).
*
* Multiple puts
and a final return
can both be
* used within a single Tcl fragment.
*
* All Tcl fragments within a given page are evaluated in the same Tcl
* interpreter. The Tcl interpreter actually lives for the entire duration
* of this Template
object, so the user can implement
* persistence across requests.
*
* The following configuration parameters are used to initialize this
* template.
* - script
*
- The name of the Tcl script to evaluate when the interpreter is
* created. This script only evaluated when the interp is created,
* not on every request. The variables
prefix
and
* server
are set before this file is evaluated, and
* are references to the parameters passed to a handler
* init method.
* - root
*
- The document root, if the script is a relative file name.
* If the "root" property under the template prefix is not found, the
* global "root" property is used. If the global "root" property is
* not found, the current directory is used.
*
- debug
*
- If this configuration parameter is present, this class
* replaces the
<server>
and
* </server>
tags with comments, so the user
* can keep track of where the dynamically generated content is coming
* from by examining the comments in the resultant HTML document.
* By default, the <server>
and
* </server>
are completely eliminated from the
* HTML document rather than changed into comments.
*
*
* Before evaluating each HTML document, this class variables
* in the Tcl interpreter, which can be used to interact back with Java to
* do things like set the response headers:
* - request
*
- Exposes the {@link sunlabs.brazil.server.Request} Java object.
* It is set anew at each request.
*
- prefix
*
- Exposes the handler prefix String.
*
- server
*
- Exposes the handler {@link sunlabs.brazil.server.Server} object.
*
- SessionId
*
- Exposes the session id for this interp, or "none".
*
*
* If a serialized version of this object is reconstituted, the init
* method must be called again.
*
* @author Colin Stevens ([email protected])
* @version 2.4
*/
public class TclServerTemplate extends Template {
private static final String SCRIPT = "script";
private static final String DEBUG = "debug";
boolean newPage;
Interp interp = null;
/*
* In a template, transient variables don't last longer than the request.
*/
transient TclTemplateChannel chan;
transient TclObject request;
transient boolean debug;
/**
* Defer setting up the interpreter until its first use.
* This way we don't initialize a tcl interp (a heavyweight
* operation) until its actually needed.
*/
public boolean
init(RewriteContext hr) {
hr.addClosingTag("tcl");
newPage=true;
return true;
}
/**
* Called at the first tcl code in the document
* TclServerTemplate
is asked to process.
*
* Redirects the standard output of the Tcl interpreter to the
* resultant HTML document and exposes the Request
* object as a Tcl variable.
*
* The first time this method is called, the initialization script is
* sourced into the interpreter, based on the configuration properties
* in the Request
*
* @param hr
* The request and associated HTML document that will be
* processed.
*
* @return true
interpreter was successfully initialized
* false
otherwise. About the only way that the
* initialization could fail would be
* - there was an error sourcing the initialization script.
*
- if this class previously evaluated a Tcl fragment that
* redefined the scalar Tcl variables
request
* or server
as array variables.
*
* If false
is returned, an error message is logged.
*/
public boolean
setup(RewriteContext hr) {
Properties props = hr.request.props;
if (interp == null) {
interp = new Interp();
String script = props.getProperty(hr.prefix + SCRIPT);
String session = (hr.sessionId == null) ? "none" : hr.sessionId;
try {
interp.eval("package require java");
interp.eval("set prefix " + hr.prefix);
interp.eval("set SessionId " + session);
TclObject server = ReflectObject.newInstance(interp,
Server.class, hr.server);
TclUtil.setVar(interp, "server", server, 0);
if (script != null) {
String body = ResourceHandler.getResourceString(props,
hr.prefix, script);
interp.eval(body);
}
} catch (IOException e) {
hr.request.log(Server.LOG_ERROR, "reading init script", script);
return false;
} catch (TclException e) {
e.printStackTrace();
if (e.getCompletionCode() != TCL.RETURN) {
hr.request.log(Server.LOG_ERROR, "loading java package",
e.toString());
return false;
}
}
}
debug = (props.getProperty(hr.prefix + DEBUG) != null);
try {
chan = new TclTemplateChannel(interp, hr);
request = ReflectObject.newInstance(interp, Request.class,
hr.request);
interp.eval("set request " + request);
} catch (TclException e) {
hr.request.log(Server.LOG_WARNING, hr.prefix,
"Setting up TCL environ: " + e.toString());
done(hr);
return false;
}
return true;
}
/**
* Called after the HTML document has been processed.
*
* Releases the resources allocated by init
. This method
* should be called to ensure that the HtmlRewriter
and
* all its attendant data structures are not preserved indefinitely
* (until the next request).
*
* @param hr
* The request and associated HTML document that was processed.
*
* @return true
always, indicating that this document was
* successfully processed to completion.
*/
public boolean
done(RewriteContext hr)
{
if (chan != null) {
chan.unregister();
}
if (request != null) {
request.release();
}
chan=null;
request=null;
return true;
}
/**
* Processes the <server>
tag. Substitues the
* result of evaluating the following Tcl script into the resultant
* HTML document.
*
* Note: Currently, there is no mechanism for other language interpreters
* to share the same server
tag. Use the
* <tcl>
tag instead.
*
* @param hr
* The request and associated HTML document that will be
* processed.
*/
public void
tag_server(RewriteContext hr) {
String server = hr.getBody();
if ("tcl".equals(hr.get("language")) == false) {
return;
}
tag_tcl(hr);
}
/**
* Processes the <tcl>
tag. Substitues the
* result of evaluating the following Tcl script into the resultant
* HTML document.
*
* @param hr
* The request and associated HTML document that will be
* processed.
*/
public void
tag_tcl(RewriteContext hr) {
debug(hr);
boolean eval = hr.isTrue("eval");
if (newPage) {
newPage=false;
setup(hr);
}
hr.accumulate(false);
hr.nextToken();
String script = hr.getBody();
if (eval) {
script = Format.subst(hr.request.props, script,true);
}
try {
interp.eval(script);
} catch (TclException e) {
if (e.getCompletionCode() == TCL.RETURN) {
String result = interp.getResult().toString();
if (result.length() > 0) {
hr.append(result);
}
} else {
hr.append("\n\n");
if (debug) {
TclObject errorInfo;
try {
errorInfo = interp.getVar("errorInfo", null,
TCL.GLOBAL_ONLY);
} catch (Exception e2) {
errorInfo = null;
}
hr.append("\n\n");
}
}
}
hr.nextToken();
if (debug) {
hr.append("");
}
hr.accumulate(true);
}
}