ste.web.http.beanshell.BeanShellHandler.java Source code

Java tutorial

Introduction

Here is the source code for ste.web.http.beanshell.BeanShellHandler.java

Source

/*
 * BeanShell Web
 * Copyright (C) 2012 Stefano Fornari
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY Stefano Fornari, Stefano Fornari
 * DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 */
package ste.web.http.beanshell;

import bsh.EvalError;
import bsh.Interpreter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;

import static ste.web.beanshell.Constants.*;
import ste.web.http.HttpSessionContext;

/**
 * Executes the bsh script specified by the URL. The script is the controller
 * and shall set the view that should be rendered after the execution of the 
 * script in the bsh variable <i>view</i>.
 * 
 * The lookup of the scripts and views are controlled by the following pattern:
 * 
 * <pre>
 *   {context}{controllers-prefix}/{script-pathname}
 *   {context}{views-prefix}/{jsp-pathname}
 * </pre>
 * 
 * For example, if context=/myapp, controllers-prefix=c and views-prefix=v,
 * the URL http://myserver:8080/myapp/mycontroller.bsh will read the script
 * {webroot}/myapp/c/mycontroller.bsh. If the controller sets <i>view</i>
 * to myview.jsp, the request is forwarded to http://myserver:8080/myapp/v/myview.jsp
 * 
 * <i>controllers-prefix</i> and <i>views-prefix</i> defauult to "".
 * 
 * In addition to scripts Beanshell can be extended with commands, which must be
 * located somewhere under the classpath.
 * 
 * @author ste
 */
public class BeanShellHandler implements HttpRequestHandler {

    // --------------------------------------------------------------- Constants

    // ------------------------------------------------------------ Private data

    private String controllersFolder;
    private String appsRoot;

    private final Logger log;

    // ------------------------------------------------------------ Constructors

    /**
     * Creates a new BeanShellHandler looking for scripts from the given webroot
     * and using the default controller folder.
     * 
     * @param webroot - NOT NULL
     */
    public BeanShellHandler(final String webroot) {
        if (webroot == null) {
            throw new IllegalArgumentException("webroot can not be null");
        }
        this.controllersFolder = null;
        this.appsRoot = webroot;
        this.log = Logger.getLogger(LOG_NAME);
    }

    public BeanShellHandler(final String webroot, final String controllerFolder) {
        this(webroot);
        setControllersFolder(controllerFolder);
    }

    // ---------------------------------------------------------- Public methods

    /**
     * @return the controllersFolder
     */
    public String getControllersFolder() {
        return controllersFolder;
    }

    /**
     * @param controllersFolder the controllersFolder to set
     */
    public void setControllersFolder(String controllersFolder) {
        this.controllersFolder = controllersFolder;
    }

    @Override
    public void handle(HttpRequest request, HttpResponse response, HttpContext context)
            throws HttpException, IOException {

        String uri = request.getRequestLine().getUri();
        int pos = uri.indexOf('?');
        if (pos >= 0) {
            uri = uri.substring(0, pos);
        }
        if (log.isLoggable(Level.FINE)) {
            log.fine(String.format("serving %s", uri));
        }

        if (controllersFolder == null) {
            controllersFolder = DEFAULT_CONTROLLERS_PREFIX;
        } else {
            //
            // let's fix a common mistake :)
            //
            if (!controllersFolder.startsWith("/")) {
                setControllersFolder('/' + getControllersFolder());
            }
        }

        File scriptFile = new File(appsRoot, uri);
        String controllerPath = scriptFile.getParent() + getControllersFolder();
        scriptFile = new File(controllerPath, scriptFile.getName());

        if (log.isLoggable(Level.FINE)) {
            log.fine(String.format("script path: %s", scriptFile.getAbsolutePath()));
        }

        try {
            Interpreter bsh = new Interpreter();
            BeanShellUtils.setup(bsh, request, response, (HttpSessionContext) context);
            bsh.set(VAR_SOURCE, scriptFile.getAbsolutePath());
            bsh.eval(BeanShellUtils.getScript(scriptFile));

            String view = (String) bsh.get(ATTR_VIEW);
            if (view == null) {
                throw new HttpException(
                        "view not defined. Set the variable 'view' to the name of the view to show (including .v).");
            }

            if (log.isLoggable(Level.FINE)) {
                log.fine("view: " + view);
            }

            BeanShellUtils.cleanup(bsh, request);
            BeanShellUtils.setVariablesAttributes(bsh, context);
        } catch (FileNotFoundException e) {
            response.setStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_NOT_FOUND,
                    "Script " + scriptFile + " not found.");
        } catch (EvalError x) {
            String msg = x.getMessage();

            if (log.isLoggable(Level.SEVERE)) {
                log.severe(String.format("error evaluating: %s: %s", uri, msg));
                log.throwing(getClass().getName(), "handleError", x);
            }
            throw new HttpException("error evaluating " + uri + ": " + msg, x);
        }
    }

    // --------------------------------------------------------- Private methods

}