
org.apache.struts.scripting.ScriptAction Maven / Gradle / Ivy
The newest version!
/*
* $Id: ScriptAction.java 471754 2006-11-06 14:55:09Z husted $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts.scripting;
// util imports:
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
// io imports:
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
// logging imports:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
// struts imports:
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessages;
// misc imports:
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;
import org.apache.bsf.util.IOUtils;
/**
* This Action uses scripts to perform its action. The scripting framework is
* Apache's Bean Scripting Framework which allows the scripts to be written
* many of the popular scripting languages including JavaScript, Perl, Python,
* and even VBA.
*
* To determine what script will be executed, the "parameter" attribute of the
* action mapping should contain the name of the script relative to the web
* application root directory (i.e. http://server/app).
*
* Before the script completes, the next ActionForward needs to be specified.
* This can be done one of two ways:
*
* - Set
struts.forwardName
to the name of the forward
*
* - Set
struts.forward
to the actual ActionForward object
*
*
* A number of pre-defined variables are available to the script:
*
* -
request
- The HTTP request
* -
response
- The HTTP response
* -
session
- The session
* -
application
- The servlet context
* -
struts
- A grouping of all Struts-related objects
*
* -
log
- A logging instance
*
* You can add your own variables by creating a BSFManagerFilter and
* configuring it in struts-scripting.properties:
*
* -
struts-scripting.filters.FILTER_NAME.class=FILTER_CLASS
* - The class implementing BSFManagerFilter where FILTER_NAME is the name
* you are calling the filter.
* -
* struts-scripting.filters.FILTER_NAME.PROPERTY_NAME=PROPERTY_VALUE
*
- A property to be used by the filter.
*
*
*
* To use other scripting engines other than BeanShell, create a file called
* struts-scripting.properties
and add two properties for each
* engine:
*
* -
struts-scripting.engine.ENGINE_NAME.class
- The class of
* the BSF engine where ENGINE_NAME is the name you are calling the engine.
*
* -
struts-scripting.engine.ENGINE_NAME.extensions
- A
* comma-delimited list of file extensions that will be used to identify the
* engine to use to execute the script.
*
* This code was originally based off code from JPublish, but has since been
* almost completely rewritten.
*/
public class ScriptAction extends Action {
/** The logging instance. */
protected static final Log LOG = LogFactory.getLog(ScriptAction.class);
/** The default path to the properties file. */
protected static final String PROPS_PATH = "/struts-scripting.properties";
/** The base property for alternate BSF engines. */
protected static final String ENGINE_BASE = "struts-scripting.engine.";
/** The base property for classes that put new variables in the context. */
protected static final String FILTERS_BASE = "struts-scripting.filters.";
/** A list of initialized filters. */
private static BSFManagerFilter[] filters = null;
/** Holds the "compiled" scripts and their information. */
private Map scripts = new Hashtable();
static {
Properties props = new Properties();
try {
InputStream in =
ScriptAction.class.getClassLoader()
.getResourceAsStream(PROPS_PATH);
if (in == null) {
in =
ScriptAction.class.getClassLoader().getResourceAsStream(
"/struts-bsf.properties");
if (in != null) {
LOG.warn("The struts-bsf.properties file has been "
+ "deprecated. Please use "
+ "struts-scripting.properties instead.");
} else {
LOG.warn("struts-scripting.properties not found, using "
+ "default engine mappings.");
}
}
if (in != null) {
props.load(in);
}
} catch (Exception ex) {
LOG.warn("Unable to load struts-scripting.properties, using "
+ " default engine mappings.");
}
int pos = ENGINE_BASE.length();
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
String name = (String) e.nextElement();
if (name.startsWith(ENGINE_BASE) && name.endsWith(".class")) {
String type = name.substring(pos, name.indexOf('.', pos));
String cls = props.getProperty(name);
String ext = props.getProperty(ENGINE_BASE + type
+ ".extensions", "");
String[] exts = split(ext, ",");
if (LOG.isInfoEnabled()) {
LOG.info("Loading BSF engine name:" + type + " class:"
+ cls + " ext:" + ext);
}
BSFManager.registerScriptingEngine(type, cls, exts);
}
}
filters = loadFilters(props);
}
/**
* Executes the script.
*
*@param mapping The action mapping
*@param form The action form
*@param request The request object
*@param response The response object
*@return The action forward
*@exception Exception If something goes wrong
*/
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
BSFManager bsfManager = new BSFManager();
String scriptName = null;
try {
scriptName = parseScriptName(mapping.getParameter(), bsfManager);
} catch (Exception ex) {
LOG.error("Unable to parse " + mapping.getParameter(), ex);
throw new Exception("Unable to parse " + mapping.getParameter());
}
if (scriptName == null) {
LOG.error("No script specified in the parameter attribute");
throw new Exception("No script specified");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Executing script: " + scriptName);
}
HttpSession session = request.getSession();
ServletContext application = getServlet().getServletContext();
Script script = loadScript(scriptName, application);
bsfManager.declareBean("request", request,
HttpServletRequest.class);
bsfManager.declareBean("response", response,
HttpServletResponse.class);
if (session == null) {
LOG.debug("HTTP session is null");
} else {
bsfManager.declareBean("session", session, HttpSession.class);
}
bsfManager.declareBean("application", application,
ServletContext.class);
bsfManager.declareBean("log", LOG, Log.class);
StrutsInfo struts = new StrutsInfo(this, mapping, form,
getResources(request));
bsfManager.declareBean("struts", struts, StrutsInfo.class);
for (int x = 0; x < filters.length; x++) {
filters[x].apply(bsfManager);
}
bsfManager.exec(script.lang, script.file.getCanonicalPath(), 0, 0,
script.string);
ActionForward af = struts.getForward();
return af;
}
/**
* Parses the script name and puts any url parameters in the context.
*
*@param url The script url consisting of a path and optional
* parameters
*@param manager The BSF manager to declare new parameters in
*@return The name of the script to execute
*@exception Exception If something goes wrong
*/
protected String parseScriptName(String url, BSFManager manager)
throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("Parsing " + url);
}
String name = null;
if (url != null) {
String[] parsed = split(url, "?");
name = parsed[0];
if (parsed.length == 2) {
if (LOG.isDebugEnabled()) {
LOG.debug("Found a query string");
}
String[] args = split(parsed[1], "&");
for (int x = 0; x < args.length; x++) {
String[] param = split(args[x], "=");
Object o = manager.lookupBean(param[0]);
if (o != null) {
LOG.warn("BSF variable " + param[0]
+ " already exists");
param[0] = "_" + param[0];
}
manager.declareBean(param[0], param[1], String.class);
if (LOG.isDebugEnabled()) {
LOG.debug("Registering param " + param[0]
+ " with value " + param[1]);
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No query string:" + parsed.length);
}
}
}
return name;
}
/**
* Loads the script from cache if possible. Reloads if the script has been
* recently modified.
*
*@param name The name of the script
*@param context The servlet context
*@return The script object
*/
protected Script loadScript(String name, ServletContext context) {
Script script = (Script) scripts.get(name);
if (script == null) {
script = new Script();
script.file = new File(context.getRealPath(name));
try {
script.lang =
BSFManager.getLangFromFilename(script.file.getName());
} catch (BSFException ex) {
LOG.warn(ex, ex);
}
}
boolean reloadScript = false;
long scriptLastModified = script.file.lastModified();
if (scriptLastModified > script.timeLastLoaded) {
if (LOG.isDebugEnabled()) {
LOG.debug("Loading updated or new script: "
+ script.file.getName());
}
reloadScript = true;
}
if (reloadScript || script.string == null) {
synchronized (this) {
script.timeLastLoaded = System.currentTimeMillis();
FileReader reader = null;
try {
reader = new FileReader(script.file);
script.string = IOUtils.getStringFromReader(reader);
} catch (IOException ex) {
LOG.error("Unable to load script: " + script.file, ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
LOG.debug(ex, ex);
}
}
}
}
}
return script;
}
/**
* Loads and initializes the filters.
*
*@param props The properties defining the filters
*@return An array of the loaded filters
*/
protected static BSFManagerFilter[] loadFilters(Properties props) {
ArrayList list = new ArrayList();
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
String prop = (String) e.nextElement();
if (prop.startsWith(FILTERS_BASE) && prop.endsWith("class")) {
String type = prop.substring(FILTERS_BASE.length(),
prop.indexOf(".", FILTERS_BASE.length()));
String claz = props.getProperty(prop);
try {
Class cls = Class.forName(claz);
BSFManagerFilter f = (BSFManagerFilter) cls.newInstance();
f.init(type, props);
list.add(f);
if (LOG.isInfoEnabled()) {
LOG.info("Loaded " + type + " filter: " + claz);
}
} catch (Exception ex) {
LOG.error("Unable to load " + type + " filter: " + claz);
}
}
}
BSFManagerFilter[] filters = new BSFManagerFilter[list.size()];
filters = (BSFManagerFilter[]) list.toArray(filters);
return filters;
}
/**
* Splits a line with the given delimiter.
*
*@param line The line to split
*@param delimiter The string to split with
*@return An array of substrings
*/
protected static String[] split(String line, String delimiter) {
if (line == null || "".equals(line)) {
return new String[]{};
}
List lst = new ArrayList();
for (Enumeration e = new StringTokenizer(line, delimiter);
e.hasMoreElements();) {
lst.add(e.nextElement());
}
String[] ret = new String[lst.size()];
return (String[]) lst.toArray(ret);
}
// These methods seem necessary as some scripting engines are not able to
// access Action's protected methods. Ugly? yes... any suggestions?
/**
* Saves a token.
*
*@param req The request object
*/
public void saveToken(HttpServletRequest req) {
super.saveToken(req);
}
/**
* Checks to see if the request is cancelled.
*
*@param req The request object
*@return True if cancelled
*/
public boolean isCancelled(HttpServletRequest req) {
return super.isCancelled(req);
}
/**
* Checks to see if the token is valid.
*
*@param req The request object
*@return True if valid
*/
public boolean isTokenValid(HttpServletRequest req) {
return super.isTokenValid(req);
}
/**
* Resets the token.
*
*@param req The request object
*/
public void resetToken(HttpServletRequest req) {
super.resetToken(req);
}
/**
* Gets the locale.
*
*@param req The request object
*@return The locale value
*/
public Locale getLocale(HttpServletRequest req) {
return super.getLocale(req);
}
/**
* Saves the messages to the request.
*
*@param req The request object
*@param mes The action messages
*/
public void saveMessages(HttpServletRequest req, ActionMessages mes) {
super.saveMessages(req, mes);
}
/**
* Saves the errors to the request.
*
*@param req The request object
*@param errs The action errors
*@deprecated Use saveErrors(HttpServletRequest, ActionMessages) instead.
* This will be removed after Struts 1.2.
*/
public void saveErrors(HttpServletRequest req, ActionErrors errs) {
super.saveErrors(req, errs);
}
/** Represents a saved script. */
class Script {
/** The script file. */
public File file;
/** The language the script is in. */
public String lang = null;
/** The time when the script was last used. */
public long timeLastLoaded = 0;
/** The contents of the script file. */
public String string = null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy