/* * VelocityFilter.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 2002-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: drach. * Portions created by drach are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): drach, suhler. * * Version: 2.3 * Created by drach on 02/04/19 * Last modified by suhler on 04/11/30 15:19:46 * * Version Histories: * * 2.3 04/11/30-15:19:46 (suhler) * fixed sccs version string * * 2.2 03/07/28-09:25:38 (suhler) * Merged changes between child workspace "/home/suhler/brazil/naws" and * parent workspace "/net/mack.eng/export/ws/brazil/naws". * * 1.5.1.1 03/07/15-09:12:31 (suhler) * doc fixes for pdf version of the manual * * 2.1 02/10/01-16:39:56 (suhler) * version change * * 1.5 02/05/29-16:29:51 (suhler) * added docs * * 1.4 02/05/17-09:43:43 (drach) * Fix so it compiles with 1.1 * * 1.3 02/05/07-07:05:09 (drach) * Add methods to allow read only access to Server and Request fields * * 1.2 02/05/02-14:56:42 (drach) * Major modifications. * * 1.2 02/04/19-15:25:05 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 1 0 velocity/VelocityFilter.java * * 1.1 02/04/19-15:25:04 (drach) * date and time created 02/04/19 15:25:04 by drach * */ package sunlabs.brazil.velocity; import java.io.StringWriter; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Enumeration; import java.util.Hashtable; import sunlabs.brazil.filter.Filter; import sunlabs.brazil.handler.ResourceHandler; import sunlabs.brazil.server.Request; import sunlabs.brazil.server.Server; import sunlabs.brazil.session.SessionManager; import sunlabs.brazil.util.Format; import sunlabs.brazil.util.http.MimeHeaders; import bsh.Interpreter; import bsh.EvalError; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.FieldMethodizer; import org.apache.velocity.app.Velocity; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.runtime.RuntimeServices; import org.apache.velocity.runtime.log.LogSystem; /** * A filter for processing markup that is a Velocity template. The filter * will "merge" the content with a BeanShell script to produce output content. *
* The following server properties are used: *
* The BeanShell script is found by first looking in the filesystem. If * not found, the classpath is searched. It's okay not to specify or * use a BeanShell script. The content will still be processed by * the Velocity engine. *
* The BeanShell interpreter is managed by session and is persistent during
* the session. The interpreter has access to the Server
* object, the prefix string, the Request
object, and the
* Velocity context through the variables "server", "prefix", "request",
* and "context".
*
* A new Velocity context is created each time the filter
* method is called. For convenience, the context is initially populated
* with the same objects as the BeanShell. They are referenced by the
* same names ("server", "prefix", "request", and "context"). However,
* due to the design of Velocity, the public fields of the Server
* and Request
objects are read only. The field values can not
* be changed by the template code.
*
* Each time the filter
method is called, a new Velocity
* context is created and populated. Then the BeanShell script, if it exists,
* is passed through Format.subst
for variable substitution
* and then passed to the BeanShell interpreter. Finally the content (i.e. a
* Velocity template) and the context are passed to the Velocity engine
* for processing.
*
* Note: Velocity requires at least a 1.2 Java VM. Attempts to use Velocity * with a 1.1 VM will most likely produce incorrect results. Also note that * the Brazil system must be compiled with a 1.2+ compiler so that core * components of Brazil are compatible with the Velocity engine. *
* @author Steve Drach <drach@sun.com>
* @version 2.3
*/
public class VelocityFilter implements Filter, LogSystem {
String script;
String prefix;
Server server;
String session;
public boolean
init(Server server, String prefix) {
this.server = server;
this.prefix = prefix;
boolean initok = true;
session = server.props.getProperty(prefix + "session", "common");
script = server.props.getProperty(prefix + "script");
if (script != null) {
try {
script = ResourceHandler.getResourceString(server.props,
prefix, script);
} catch (IOException e) {
log(Server.LOG_ERROR, "BeanShell IOException: ", e);
initok = false;
}
}
if (initok && script != null) {
Interpreter bsh = (Interpreter)SessionManager.getSession(session,
prefix, Interpreter.class);
try {
bsh.set("server", server);
bsh.set("prefix", prefix);
} catch (EvalError e) {
log(Server.LOG_WARNING, "BeanShell EvalError: ", e);
initok = false;
}
}
if (initok) {
try {
Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, this);
Velocity.init();
} catch (Exception e) {
log(Server.LOG_ERROR, "Velocity Exception: ", e);
initok = false;
}
}
return initok;
}
/**
* This is the request object before the content was fetched
*/
public boolean respond(Request request) {
return false;
}
/**
* Only filter text/* documents
*/
public boolean
shouldFilter(Request request, MimeHeaders headers) {
String type = headers.get("content-type");
return (type != null && type.toLowerCase().startsWith("text/"));
}
/**
* Execute the BeanShell script if it exists and then process the
* content as a Velocity template.
*/
public byte[]
filter(Request request, MimeHeaders headers, byte[] content) {
boolean aok = true;
VelocityContext context = new VelocityContext();
context.put("context", context);
context.put("prefix", prefix);
try {
context.put("request", new Vrequest(request));
} catch (Exception e) {
log(Server.LOG_WARNING, "Context error with request: ", e);
context.put("request", request);
}
try {
context.put("server", new Vserver(server));
} catch (Exception e) {
log(Server.LOG_WARNING, "Context error with server: ", e);
context.put("server", server);
}
if (script != null) {
Interpreter bsh = (Interpreter)SessionManager.getSession(session,
prefix, null);
try {
bsh.set("context", context);
bsh.set("request", request);
bsh.eval(Format.subst(request.props, script));
} catch (EvalError e) {
log(Server.LOG_WARNING, "BeanShell EvalError: ", e);
aok = false;
}
}
StringWriter w = new StringWriter();
if (aok) {
try {
Velocity.evaluate(context, w, "Velocity", new String(content));
} catch (ResourceNotFoundException e) {
log(Server.LOG_WARNING, "Velocity ResourceNotFoundException: ", e);
aok = false;
} catch (IOException e) {
log(Server.LOG_WARNING, "Velocity IOException: ", e);
aok = false;
} catch (MethodInvocationException e) {
log(Server.LOG_WARNING, "Velocity MethodInvocationException: ", e);
aok = false;
} catch (ParseErrorException e) {
log(Server.LOG_WARNING, "Velocity ParseErrorException: ", e);
aok = false;
}
}
if (aok) {
return w.toString().getBytes();
} else {
return content;
}
}
private void log(int level, String msg, Exception e) {
if (e == null) {
server.log(level, prefix, msg);
} else {
server.log(level, prefix, msg + e.getMessage());
}
}
/**
* Velocity LogSystem interface implementation.
*/
public void init(RuntimeServices rsvc) {
}
public void logVelocityMessage(int level, String message) {
switch(level) {
case LogSystem.INFO_ID:
level = Server.LOG_INFORMATIONAL;
break;
case LogSystem.WARN_ID:
level = Server.LOG_WARNING;
break;
case LogSystem.ERROR_ID:
level = Server.LOG_ERROR;
break;
}
log(level, message, null);
}
/*
* These classes make public fields accessable like Properties keys.
* Unfortunately, this is read-only since Velocity only looks for
* put methods on instances of Maps.
*/
private Hashtable serverFields;
/**
* A helper class for Velocity that provides read only access
* to the public fields of the Server
object. This
* class and it's methods should only be used by the Velocity
* engine.
*/
public class Vserver extends Server {
public Vserver(Server server) throws Exception {
if (serverFields == null) {
serverFields = new Hashtable();
Field[] fields = getClass().getFields();
for (int i = 0; i < fields.length; i++) {
serverFields.put(fields[i].getName(), fields[i]);
}
}
Enumeration e = serverFields.elements();
while (e.hasMoreElements()) {
Field field = (Field)(e.nextElement());
if (!Modifier.isFinal(field.getModifiers())) {
field.set(this, field.get(server));
}
}
}
public Object get(String fieldName) {
try {
Field f = (Field)(serverFields.get(fieldName));
if (f != null) {
return f.get(this);
}
} catch(Exception e) {
this.log(Server.LOG_WARNING, "Vserver get: ", e.getMessage());
}
return null;
}
}
private Hashtable requestFields;
/**
* A helper class for Velocity that provides read only access
* to the public fields of the Request
object. This
* class and it's methods should only be used by the Velocity
* engine.
*/
public class Vrequest extends Request {
public Vrequest(Request request) throws Exception {
if (requestFields == null) {
requestFields = new Hashtable();
Field[] fields = getClass().getFields();
for (int i = 0; i < fields.length; i++) {
requestFields.put(fields[i].getName(), fields[i]);
}
}
Enumeration e = requestFields.elements();
while (e.hasMoreElements()) {
Field field = (Field)(e.nextElement());
if (!Modifier.isFinal(field.getModifiers())) {
field.set(this, field.get(request));
}
}
}
public Object get(String fieldName) {
try {
Field f = (Field)(requestFields.get(fieldName));
if (f != null) {
return f.get(this);
}
} catch(Exception e) {
this.log(Server.LOG_WARNING, "Vrequest get: ", e.getMessage());
}
return null;
}
}
}