/*
 * Helma License Notice
 *
 * The contents of this file are subject to the Helma License
 * Version 2.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://adele.invoker.org/download/invoker/license.txt
 *
 * Copyright 2005 Hannes Wallnoefer. All Rights Reserved.
 */

package org.helma.web;

import org.helma.javascript.RhinoEngine;
import org.helma.template.MacroTag;
import org.helma.tools.HelmaConfiguration;
import org.helma.util.StringUtils;
import org.helma.repository.FileRepository;
import org.helma.repository.Repository;
import org.helma.repository.WebappRepository;
import org.mozilla.javascript.WrappedException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.concurrent.*;

/**
 * Helma servlet class used to access helma from a web server.
 */
public class HelmaServlet extends HttpServlet {

    protected RhinoEngine engine;

    private ExecutorService pool;

    private int requestTimeout = 30;

    private String moduleName, functionName;

    static protected Class[] defaultHostClasses =
        new Class[] {
            Request.class,
            Response.class,
            Session.class,
            MacroTag.class
        };

    private static final long serialVersionUID = 9078627193637127187L;

    /**
     * Zero argument constructor used by servlet config.
     */
    public HelmaServlet() {}

    /**
     * Build a servlet around an already existing rhino engine.
     * This is used when starting the server from a script or shell.
     * @param engine a rhino engine
     */
    public HelmaServlet(RhinoEngine engine) {
        this.engine = engine;
    }

    public void init(ServletConfig servletConfig) throws ServletException {
        // pool = Executors.newFixedThreadPool(8);
        pool = Executors.newCachedThreadPool();
        moduleName = servletConfig.getInitParameter("moduleName");
        if (moduleName == null) {
            throw new ServletException("moduleName servlet parameter not defined");
        }
        functionName = servletConfig.getInitParameter("functionName");
        if (functionName == null) {
            throw new ServletException("functionName servlet parameter not defined");
        }
        String timeout = servletConfig.getInitParameter("requestTimeout");
        if (timeout != null) {
            requestTimeout = Integer.parseInt(timeout);
        }
        if (engine == null) {
            try {
                String classNames = servletConfig.getInitParameter("hostClasses");
                Class[] classes = defaultHostClasses;
                if (classNames != null) {
                    Class[] custom = StringUtils.toClassArray(classNames, ", ");
                    Class[] copy = new Class[classes.length + custom.length];
                    System.arraycopy(classes, 0, copy, 0, classes.length);
                    System.arraycopy(custom, 0, copy, classes.length, custom.length);
                    classes = copy;
                }
                String helmaHome = servletConfig.getInitParameter("helmaHome");
                String modulePath = servletConfig.getInitParameter("modulePath");
                Repository home = new FileRepository(helmaHome);
                if (!home.exists()) {
                    home = new WebappRepository(servletConfig.getServletContext(), helmaHome);
                }
                HelmaConfiguration config =
                        new HelmaConfiguration(home, modulePath, "modules");
                config.setHostClasses(classes);
                engine = new RhinoEngine(config, null);
            } catch (ClassNotFoundException x) {
                throw new ServletException(x);
            } catch (FileNotFoundException x) {
                throw new ServletException(x);
            }
        }
    }

    protected void service(final HttpServletRequest req,
                           final HttpServletResponse res)
            throws ServletException, IOException {
        Future<Status> future = pool.submit(new Callable<Status>() {
            public Status call() {
                Status status = new Status();
                try {
                    engine.invoke(moduleName, functionName, new Request(req), new Response(res));
                } catch (RedirectException redir) {
                    status.redirect = redir.getMessage();
                } catch (WrappedException wx) {
                    status.exception = wx.getWrappedException();
                } catch (Exception x) {
                    status.exception = x;
                }
                return status;
            }
        });
        try {
            Status status = future.get(requestTimeout, TimeUnit.SECONDS);
            if (status.redirect != null) {
                res.sendRedirect(status.redirect);
            } else if (status.exception != null) {
                throw new ServletException(status.exception);
            }
        } catch (InterruptedException x) {
            throw new ServletException("Interrupted", x);
        } catch (ExecutionException x) {
            throw new ServletException("Execution Error", x);
        } catch (TimeoutException x) {
            throw new ServletException("Request timed out", x);
        }
    }

    public int getRequestTimeout() {
        return requestTimeout;
    }

    public void setRequestTimeout(int requestTimeout) {
        this.requestTimeout = requestTimeout;
    }

    class Status {
        volatile Throwable exception;
        volatile String redirect;
    }
    
}

@@ -658,56 +658,8 @@
 ory;
-%0Aimport org.mozilla.javascript.WrappedException;
 %0A%0Aim
@@ -932,39 +932,8 @@
 ion;
-%0Aimport java.util.concurrent.*;
 %0A%0A/*
@@ -1089,43 +1089,8 @@
 e;%0A%0A
-    private ExecutorService pool;%0A%0A
     
@@ -1885,107 +1885,8 @@
 n %7B%0A
-        // pool = Executors.newFixedThreadPool(8);%0A        pool = Executors.newCachedThreadPool();%0A
     
@@ -4055,180 +4055,14 @@
     
-Future%3CStatus%3E future = pool.submit(new Callable%3CStatus%3E() %7B%0A            public Status call() %7B%0A                Status status = new Status();%0A                try %7B%0A        
+try %7B%0A
     
@@ -4147,24 +4147,16 @@
 (res));%0A
-        
         
@@ -4207,34 +4207,25 @@
     
-        status.r
+res.sendR
 edirect
- = 
+(
 redi
@@ -4242,138 +4242,19 @@
 ge()
+)
 ;%0A
-                %7D catch (WrappedException wx) %7B%0A                    status.exception = wx.getWrappedException();%0A       
         
  %7D c
@@ -4241,33 +4241,32 @@
 age());%0A        
- 
 %7D catch (Excepti
@@ -4262,749 +4262,91 @@
 ch (
-Exception x) %7B%0A                    status.exception = x;%0A                %7D%0A                return status;%0A            %7D%0A        %7D);%0A        try %7B%0A            Status status = future.get(requestTimeout, TimeUnit.SECONDS);%0A            if (status.redirect != null) %7B%0A                res.sendRedirect(status.redirect);%0A            %7D else if (status.exception != null) %7B%0A                throw new ServletException(status.exception);%0A            %7D%0A        %7D catch (InterruptedException x) %7B%0A            throw new ServletException(%22Interrupted%22, x);%0A        %7D catch (ExecutionException x) %7B%0A            throw new ServletException(%22Execution Error%22, x);%0A        %7D catch (TimeoutException x) %7B%0A            throw new ServletException(%22Request timed out
+NoSuchMethodException x) %7B%0A            throw new ServletException(%22Method not found
 %22, x

Simplify HelmaServlet for now by removing all the advanced thread-pool stuff, because it won't run on Google App Engine. If needed, it can be added later, or in a specialized subclass.