]> source.dussan.org Git - vaadin-framework.git/commitdiff
Merges from 6.0 branch.
authorJouni Koivuviita <jouni.koivuviita@itmill.com>
Wed, 6 May 2009 11:14:08 +0000 (11:14 +0000)
committerJouni Koivuviita <jouni.koivuviita@itmill.com>
Wed, 6 May 2009 11:14:08 +0000 (11:14 +0000)
svn changeset:7637/svn branch:theme_2009_03

src/com/itmill/toolkit/launcher/WidgetsetCompiler.java [new file with mode: 0644]
src/com/itmill/toolkit/terminal/gwt/client/BrowserInfo.java
src/com/itmill/toolkit/terminal/gwt/client/ICaption.java
src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java
src/com/itmill/toolkit/terminal/gwt/client/ui/ITabsheet.java
src/com/itmill/toolkit/terminal/gwt/server/AbstractApplicationServlet.java [new file with mode: 0644]
src/com/itmill/toolkit/terminal/gwt/server/ApplicationRunnerServlet.java [new file with mode: 0644]
src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java
src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java
src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java

diff --git a/src/com/itmill/toolkit/launcher/WidgetsetCompiler.java b/src/com/itmill/toolkit/launcher/WidgetsetCompiler.java
new file mode 100644 (file)
index 0000000..389aaca
--- /dev/null
@@ -0,0 +1,55 @@
+package com.itmill.toolkit.launcher;\r
+\r
+import com.google.gwt.dev.GWTCompiler;\r
+\r
+/**\r
+ * A wrapper for the GWT 1.6 compiler that runs the compiler in a new thread.\r
+ * \r
+ * This allows circumventing a J2SE 5.0 bug (6316197) that prevents setting the\r
+ * stack size for the main thread. Thus, larger widgetsets can be compiled.\r
+ * \r
+ * This class takes the same command line arguments as the\r
+ * com.google.gwt.dev.GWTCompiler class. The old and deprecated compiler is used\r
+ * for compatibility with GWT 1.5.\r
+ * \r
+ * A typical invocation would use e.g. the following arguments\r
+ * \r
+ * "-out WebContent/ITMILL/widgetsets com.itmill.toolkit.terminal.gwt.DefaultWidgetSet"\r
+ * \r
+ * In addition, larger memory usage settings for the VM should be used, e.g.\r
+ * \r
+ * "-Xms256M -Xmx512M -Xss8M"\r
+ * \r
+ * The source directory containing widgetset and related classes must be\r
+ * included in the classpath, as well as the gwt-dev-[platform].jar and other\r
+ * relevant JARs.\r
+ */\r
+public class WidgetsetCompiler {\r
+\r
+    /**\r
+     * @param args\r
+     *            same arguments as for com.google.gwt.dev.Compiler\r
+     */\r
+    public static void main(final String[] args) {\r
+        try {\r
+            // run the compiler in a different thread to enable using the\r
+            // user-set stack size\r
+\r
+            // on Windows, the default stack size is too small for the main\r
+            // thread and cannot be changed in JRE 1.5 (see\r
+            // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6316197)\r
+\r
+            Runnable runCompiler = new Runnable() {\r
+                public void run() {\r
+                    GWTCompiler.main(args);\r
+                }\r
+            };\r
+            Thread runThread = new Thread(runCompiler);\r
+            runThread.start();\r
+            runThread.join();\r
+            System.out.println("Widgetset compilation finished");\r
+        } catch (Throwable thr) {\r
+            thr.printStackTrace();\r
+        }\r
+    }\r
+}\r
index 42e44d037455abebe41a60aa82bd11242fc43193..1604ac65d135fac963b928bd7d36293a26fdf38f 100644 (file)
@@ -74,6 +74,11 @@ public class BrowserInfo {
                 ieVersionString = ieVersionString.substring(0, ieVersionString
                         .indexOf(";"));
                 version = Float.parseFloat(ieVersionString);
+
+                if (version == 8 && isIE8InIE7CompatibilityMode()) {
+                    version = 7;
+                }
+
             }
         } catch (Exception e) {
             ClientExceptionHandler.displayError(e);
@@ -100,7 +105,6 @@ public class BrowserInfo {
      */
     public String getCSSClass() {
         String prefix = "i-";
-        boolean ie8 = false;
 
         if (cssClass == null) {
             String bs = getBrowserString().toLowerCase();
@@ -122,29 +126,32 @@ public class BrowserInfo {
                 b = "ie";
                 int i = bs.indexOf(" msie ") + 6;
                 String ieVersion = bs.substring(i, i + 1);
-                v = b + ieVersion;
 
-                // This adds .i-ie7 for ie8 also.
-                // TODO Remove this when IE8 is no longer run in compatibility
-                // mode
-                if (ieVersion != null && ieVersion.equals("8")) {
-                    ie8 = true;
+                if (ieVersion != null && ieVersion.equals("8")
+                        && isIE8InIE7CompatibilityMode()) {
+                    ieVersion = "7";
                 }
 
+                v = b + ieVersion;
             } else if (bs.indexOf("opera/") != -1) {
                 b = "op";
                 int i = bs.indexOf("opera/") + 6;
                 v = b + bs.substring(i, i + 3).replace(".", "");
             }
             cssClass = prefix + b + " " + prefix + v;
-            if (ie8) {
-                cssClass += " " + prefix + "ie7";
-            }
         }
 
         return cssClass;
     }
 
+    private native boolean isIE8InIE7CompatibilityMode()
+    /*-{
+        var mode = $wnd.document.documentMode;
+        if (!mode)
+            return false;
+        return (mode == 7);
+    }-*/;
+
     public boolean isIE() {
         return isIE;
     }
@@ -161,6 +168,10 @@ public class BrowserInfo {
         return isIE && version == 7;
     }
 
+    public boolean isIE8() {
+        return isIE && version == 8;
+    }
+
     public boolean isGecko() {
         return isGecko;
     }
@@ -201,6 +212,7 @@ public class BrowserInfo {
         c.log("isIE() " + get().isIE());
         c.log("isIE6() " + get().isIE6());
         c.log("isIE7() " + get().isIE7());
+        c.log("isIE8() " + get().isIE8());
         c.log("isFF2() " + get().isFF2());
         c.log("isSafari() " + get().isSafari());
         c.log("getGeckoVersion() " + get().getGeckoVersion());
index d3e12769ad1b84a42299615dc1f04f7b5a185d1f..7487ecc4345870c4bd101988369e63cc92f18653 100644 (file)
@@ -436,4 +436,8 @@ public class ICaption extends HTML {
         }
     }
 
-}
+    protected Element getTextElement() {
+        return captionText;
+    }
+
+}
\ No newline at end of file
index 8b108549597289c83d2bc97e698691b3d962135b..a64a41fa7fd71701aa284136dee0733e22d177f6 100644 (file)
@@ -18,6 +18,7 @@ import com.google.gwt.user.client.ui.Widget;
 import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
 import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
 import com.itmill.toolkit.terminal.gwt.client.Container;
+import com.itmill.toolkit.terminal.gwt.client.ICaption;
 import com.itmill.toolkit.terminal.gwt.client.Paintable;
 import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
 import com.itmill.toolkit.terminal.gwt.client.UIDL;
@@ -113,8 +114,9 @@ public class IAbsoluteLayout extends ComplexPanel implements Container {
     }
 
     public void updateCaption(Paintable component, UIDL uidl) {
-        // TODO Auto-generated method stub
-
+        AbsoluteWrapper parent2 = (AbsoluteWrapper) ((Widget) component)
+                .getParent();
+        parent2.updateCaption(uidl);
     }
 
     public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
@@ -189,9 +191,10 @@ public class IAbsoluteLayout extends ComplexPanel implements Container {
 
     private void relayoutRelativeChildren() {
         for (Widget widget : getChildren()) {
-            if (widget instanceof Paintable) {
-                Paintable new_name = (Paintable) widget;
-                client.handleComponentRelativeSize(widget);
+            if (widget instanceof AbsoluteWrapper) {
+                AbsoluteWrapper w = (AbsoluteWrapper) widget;
+                client.handleComponentRelativeSize(w.getWidget());
+                w.updateCaptionPosition();
             }
         }
     }
@@ -213,7 +216,9 @@ public class IAbsoluteLayout extends ComplexPanel implements Container {
 
     private void relayoutWrappersForIe6() {
         for (Widget wrapper : getChildren()) {
-            ((AbsoluteWrapper) wrapper).ie6Layout();
+            if (wrapper instanceof AbsoluteWrapper) {
+                ((AbsoluteWrapper) wrapper).ie6Layout();
+            }
         }
     }
 
@@ -226,13 +231,35 @@ public class IAbsoluteLayout extends ComplexPanel implements Container {
         private String zIndex;
 
         private Paintable paintable;
+        private ICaption caption;
 
         public AbsoluteWrapper(Paintable paintable) {
             this.paintable = paintable;
             setStyleName(CLASSNAME + "-wrapper");
         }
 
+        public void updateCaption(UIDL uidl) {
+
+            boolean captionIsNeeded = ICaption.isNeeded(uidl);
+            if (captionIsNeeded) {
+                if (caption == null) {
+                    caption = new ICaption(paintable, client);
+                    IAbsoluteLayout.this.add(caption);
+                }
+                caption.updateCaption(uidl);
+                updateCaptionPosition();
+            } else {
+                if (caption != null) {
+                    caption.removeFromParent();
+                    caption = null;
+                }
+            }
+        }
+
         public void destroy() {
+            if (caption != null) {
+                caption.removeFromParent();
+            }
             client.unregisterPaintable(paintable);
             removeFromParent();
         }
@@ -285,7 +312,17 @@ public class IAbsoluteLayout extends ComplexPanel implements Container {
                     ie6Layout();
                 }
             }
+            updateCaptionPosition();
+        }
 
+        private void updateCaptionPosition() {
+            if (caption != null) {
+                Style style = caption.getElement().getStyle();
+                style.setProperty("position", "absolute");
+                style.setPropertyPx("left", getElement().getOffsetLeft());
+                style.setPropertyPx("top", getElement().getOffsetTop()
+                        - caption.getHeight());
+            }
         }
 
         private void ie6Layout() {
index 519ec6034a7c58d35228bbecbfa6e0f3ae5d8cca..e40b6ac0a81406e3b4976535bff9a26c52153d1e 100644 (file)
@@ -45,6 +45,29 @@ public class ITabsheet extends ITabsheetBase {
             }
         }
 
+        @Override
+        public void setWidth(String width) {
+            super.setWidth(width);
+            if (BrowserInfo.get().isIE7()) {
+                /*
+                 * IE7 apparently has problems with calculating width for
+                 * floated elements inside a DIV with padding. Set the width
+                 * explicitly for the caption.
+                 */
+                fixTextWidth();
+            }
+        }
+
+        private void fixTextWidth() {
+            Element captionText = getTextElement();
+            int captionWidth = Util.getRequiredWidth(captionText);
+            int scrollWidth = captionText.getScrollWidth();
+            if (scrollWidth > captionWidth) {
+                captionWidth = scrollWidth;
+            }
+            captionText.getStyle().setPropertyPx("width", captionWidth);
+        }
+
     }
 
     class TabBar extends ComplexPanel implements ClickListener {
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/AbstractApplicationServlet.java
new file mode 100644 (file)
index 0000000..963550d
--- /dev/null
@@ -0,0 +1,1797 @@
+package com.itmill.toolkit.terminal.gwt.server;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.xml.sax.SAXException;
+
+import com.itmill.toolkit.Application;
+import com.itmill.toolkit.Application.SystemMessages;
+import com.itmill.toolkit.external.org.apache.commons.fileupload.servlet.ServletFileUpload;
+import com.itmill.toolkit.service.FileTypeResolver;
+import com.itmill.toolkit.terminal.DownloadStream;
+import com.itmill.toolkit.terminal.ParameterHandler;
+import com.itmill.toolkit.terminal.Terminal;
+import com.itmill.toolkit.terminal.ThemeResource;
+import com.itmill.toolkit.terminal.URIHandler;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.ui.Window;
+
+/**
+ * Abstract implementation of the ApplicationServlet which handles all
+ * communication between the client and the server.
+ * 
+ * It is possible to extend this class to provide own functionality but in most
+ * cases this is unnecessary.
+ * 
+ * 
+ * @author IT Mill Ltd.
+ * @version
+ * @VERSION@
+ * @since 6.0
+ */
+
+@SuppressWarnings("serial")
+public abstract class AbstractApplicationServlet extends HttpServlet {
+    /**
+     * Version number of this release. For example "5.0.0".
+     */
+    public static final String VERSION;
+    /**
+     * Major version number. For example 5 in 5.1.0.
+     */
+    public static final int VERSION_MAJOR;
+
+    /**
+     * Minor version number. For example 1 in 5.1.0.
+     */
+    public static final int VERSION_MINOR;
+
+    /**
+     * Builds number. For example 0-custom_tag in 5.0.0-custom_tag.
+     */
+    public static final String VERSION_BUILD;
+
+    /* Initialize version numbers from string replaced by build-script. */
+    static {
+        if ("@VERSION@".equals("@" + "VERSION" + "@")) {
+            VERSION = "5.9.9-INTERNAL-NONVERSIONED-DEBUG-BUILD";
+        } else {
+            VERSION = "@VERSION@";
+        }
+        final String[] digits = VERSION.split("\\.");
+        VERSION_MAJOR = Integer.parseInt(digits[0]);
+        VERSION_MINOR = Integer.parseInt(digits[1]);
+        VERSION_BUILD = digits[2];
+    }
+
+    /**
+     * If the attribute is present in the request, a html fragment will be
+     * written instead of a whole page.
+     */
+    public static final String REQUEST_FRAGMENT = ApplicationServlet.class
+            .getName()
+            + ".fragment";
+    /**
+     * This request attribute forces widgetset used; e.g for portlets that can
+     * not have different widgetsets.
+     */
+    public static final String REQUEST_WIDGETSET = ApplicationServlet.class
+            .getName()
+            + ".widgetset";
+    /**
+     * This request attribute is used to add styles to the main element. E.g
+     * "height:500px" generates a style="height:500px" to the main element,
+     * useful from some embedding situations (e.g portlet include.)
+     */
+    public static final String REQUEST_APPSTYLE = ApplicationServlet.class
+            .getName()
+            + ".style";
+
+    private Properties applicationProperties;
+
+    private static final String NOT_PRODUCTION_MODE_INFO = "=================================================================\nIT Mill Toolkit is running in DEBUG MODE.\nAdd productionMode=true to web.xml to disable debug features.\nTo show debug window, add ?debug to your application URL.\n=================================================================";
+
+    private boolean productionMode = false;
+
+    private static final String URL_PARAMETER_RESTART_APPLICATION = "restartApplication";
+    private static final String URL_PARAMETER_CLOSE_APPLICATION = "closeApplication";
+    private static final String URL_PARAMETER_REPAINT_ALL = "repaintAll";
+    protected static final String URL_PARAMETER_THEME = "theme";
+
+    private static final String SERVLET_PARAMETER_DEBUG = "Debug";
+    private static final String SERVLET_PARAMETER_PRODUCTION_MODE = "productionMode";
+
+    // Configurable parameter names
+    private static final String PARAMETER_ITMILL_RESOURCES = "Resources";
+
+    private static final int DEFAULT_BUFFER_SIZE = 32 * 1024;
+
+    private static final int MAX_BUFFER_SIZE = 64 * 1024;
+
+    private static final String RESOURCE_URI = "/RES/";
+
+    private static final String AJAX_UIDL_URI = "/UIDL";
+
+    static final String THEME_DIRECTORY_PATH = "ITMILL/themes/";
+
+    private static final int DEFAULT_THEME_CACHETIME = 1000 * 60 * 60 * 24;
+
+    static final String WIDGETSET_DIRECTORY_PATH = "ITMILL/widgetsets/";
+
+    // Name of the default widget set, used if not specified in web.xml
+    private static final String DEFAULT_WIDGETSET = "com.itmill.toolkit.terminal.gwt.DefaultWidgetSet";
+
+    // Widget set parameter name
+    private static final String PARAMETER_WIDGETSET = "widgetset";
+
+    private static final String ERROR_NO_WINDOW_FOUND = "Application did not give any window, did you remember to setMainWindow()?";
+
+    private String resourcePath = null;
+
+    /**
+     * Called by the servlet container to indicate to a servlet that the servlet
+     * is being placed into service.
+     * 
+     * @param servletConfig
+     *            the object containing the servlet's configuration and
+     *            initialization parameters
+     * @throws javax.servlet.ServletException
+     *             if an exception has occurred that interferes with the
+     *             servlet's normal operation.
+     */
+    @Override
+    public void init(javax.servlet.ServletConfig servletConfig)
+            throws javax.servlet.ServletException {
+        super.init(servletConfig);
+
+        // Stores the application parameters into Properties object
+        applicationProperties = new Properties();
+        for (final Enumeration e = servletConfig.getInitParameterNames(); e
+                .hasMoreElements();) {
+            final String name = (String) e.nextElement();
+            applicationProperties.setProperty(name, servletConfig
+                    .getInitParameter(name));
+        }
+
+        // Overrides with server.xml parameters
+        final ServletContext context = servletConfig.getServletContext();
+        for (final Enumeration e = context.getInitParameterNames(); e
+                .hasMoreElements();) {
+            final String name = (String) e.nextElement();
+            applicationProperties.setProperty(name, context
+                    .getInitParameter(name));
+        }
+
+        checkProductionMode();
+    }
+
+    private void checkProductionMode() {
+        // Check if the application is in production mode.
+        // We are in production mode if Debug=false or productionMode=true
+        if (getApplicationOrSystemProperty(SERVLET_PARAMETER_DEBUG, "true")
+                .equals("false")) {
+            // "Debug=true" is the old way and should no longer be used
+            productionMode = true;
+        } else if (getApplicationOrSystemProperty(
+                SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals("true")) {
+            // "productionMode=true" is the real way to do it
+            productionMode = true;
+        }
+
+        if (!productionMode) {
+            /* Print an information/warning message about running in debug mode */
+            System.err.println(NOT_PRODUCTION_MODE_INFO);
+        }
+
+    }
+
+    /**
+     * Gets an application property value.
+     * 
+     * @param parameterName
+     *            the Name or the parameter.
+     * @return String value or null if not found
+     */
+    protected String getApplicationProperty(String parameterName) {
+
+        String val = applicationProperties.getProperty(parameterName);
+        if (val != null) {
+            return val;
+        }
+
+        // Try lower case application properties for backward compatibility with
+        // 3.0.2 and earlier
+        val = applicationProperties.getProperty(parameterName.toLowerCase());
+
+        return val;
+    }
+
+    /**
+     * Gets an system property value.
+     * 
+     * @param parameterName
+     *            the Name or the parameter.
+     * @return String value or null if not found
+     */
+    protected String getSystemProperty(String parameterName) {
+        String val = null;
+
+        String pkgName;
+        final Package pkg = getClass().getPackage();
+        if (pkg != null) {
+            pkgName = pkg.getName();
+        } else {
+            final String className = getClass().getName();
+            pkgName = new String(className.toCharArray(), 0, className
+                    .lastIndexOf('.'));
+        }
+        val = System.getProperty(pkgName + "." + parameterName);
+        if (val != null) {
+            return val;
+        }
+
+        // Try lowercased system properties
+        val = System.getProperty(pkgName + "." + parameterName.toLowerCase());
+        return val;
+    }
+
+    /**
+     * Gets an application or system property value.
+     * 
+     * @param parameterName
+     *            the Name or the parameter.
+     * @param defaultValue
+     *            the Default to be used.
+     * @return String value or default if not found
+     */
+    private String getApplicationOrSystemProperty(String parameterName,
+            String defaultValue) {
+
+        String val = null;
+
+        // Try application properties
+        val = getApplicationProperty(parameterName);
+        if (val != null) {
+            return val;
+        }
+
+        // Try system properties
+        val = getSystemProperty(parameterName);
+        if (val != null) {
+            return val;
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Returns true if the servlet is running in production mode. Production
+     * mode disables all debug facilities.
+     * 
+     * @return true if in production mode, false if in debug mode
+     */
+    public boolean isProductionMode() {
+        return productionMode;
+    }
+
+    /**
+     * Receives standard HTTP requests from the public service method and
+     * dispatches them.
+     * 
+     * @param request
+     *            the object that contains the request the client made of the
+     *            servlet.
+     * @param response
+     *            the object that contains the response the servlet returns to
+     *            the client.
+     * @throws ServletException
+     *             if an input or output error occurs while the servlet is
+     *             handling the TRACE request.
+     * @throws IOException
+     *             if the request for the TRACE cannot be handled.
+     */
+    @Override
+    protected void service(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // check if we should serve static files (widgetsets, themes)
+        if (serveStaticResources(request, response)) {
+            return;
+        }
+
+        Application application = null;
+        RequestType requestType = getRequestType(request);
+
+        try {
+            // Find out which application this request is related to
+            application = findApplicationInstance(request, requestType);
+            if (application == null) {
+                return;
+            }
+
+            /*
+             * Get or create a WebApplicationContext and an ApplicationManager
+             * for the session
+             */
+            WebApplicationContext webApplicationContext = WebApplicationContext
+                    .getApplicationContext(request.getSession());
+            CommunicationManager applicationManager = webApplicationContext
+                    .getApplicationManager(application, this);
+
+            /* Update browser information from the request */
+            webApplicationContext.getBrowser().updateBrowserProperties(request);
+
+            /*
+             * Transaction starts. Call transaction listeners. Transaction end
+             * is called in the finally block below.
+             */
+            webApplicationContext.startTransaction(application, request);
+
+            // TODO Add screen height and width to the GWT client
+
+            /* Handle the request */
+            if (requestType == RequestType.FILE_UPLOAD) {
+                applicationManager.handleFileUpload(request, response);
+                return;
+            } else if (requestType == RequestType.UIDL) {
+                // Handles AJAX UIDL requests
+                applicationManager.handleUidlRequest(request, response, this);
+                return;
+            }
+
+            // Removes application if it has stopped
+            if (!application.isRunning()) {
+                // FIXME How can this be reached?
+                endApplication(request, response, application);
+                return;
+            }
+
+            // Finds the window within the application
+            Window window = getApplicationWindow(request, application);
+            if (window == null) {
+                throw new ServletException(ERROR_NO_WINDOW_FOUND);
+            }
+
+            // Sets terminal type for the window, if not already set
+            if (window.getTerminal() == null) {
+                window.setTerminal(webApplicationContext.getBrowser());
+            }
+
+            // Handle parameters
+            final Map parameters = request.getParameterMap();
+            if (window != null && parameters != null) {
+                window.handleParameters(parameters);
+            }
+
+            /*
+             * Call the URI handlers and if this turns out to be a download
+             * request, send the file to the client
+             */
+            if (handleURI(applicationManager, window, request, response)) {
+                return;
+            }
+
+            String themeName = getThemeForWindow(request, window);
+
+            // Handles theme resource requests
+            if (handleResourceRequest(request, response, themeName)) {
+                return;
+            }
+
+            // Send initial AJAX page that kickstarts Toolkit application
+            writeAjaxPage(request, response, window, themeName, application);
+
+        } catch (final SessionExpired e) {
+            // Session has expired, notify user
+            handleServiceSessionExpired(request, response);
+        } catch (final GeneralSecurityException e) {
+            handleServiceSecurityException(request, response);
+        } catch (final Throwable e) {
+            handleServiceException(request, response, application, e);
+        } finally {
+            // Notifies transaction end
+            if (application != null) {
+                ((WebApplicationContext) application.getContext())
+                        .endTransaction(application, request);
+            }
+
+            // Work-around for GAE session problem. Explicitly touch session so
+            // it is re-serialized.
+            HttpSession session = request.getSession(false);
+            if (session != null) {
+                session.setAttribute("sessionUpdated", new Date().getTime());
+            }
+        }
+    }
+
+    protected ClassLoader getClassLoader() throws ServletException {
+        // Gets custom class loader
+        final String classLoaderName = getApplicationOrSystemProperty(
+                "ClassLoader", null);
+        ClassLoader classLoader;
+        if (classLoaderName == null) {
+            classLoader = getClass().getClassLoader();
+        } else {
+            try {
+                final Class<?> classLoaderClass = getClass().getClassLoader()
+                        .loadClass(classLoaderName);
+                final Constructor<?> c = classLoaderClass
+                        .getConstructor(new Class[] { ClassLoader.class });
+                classLoader = (ClassLoader) c
+                        .newInstance(new Object[] { getClass().getClassLoader() });
+            } catch (final Exception e) {
+                throw new ServletException(
+                        "Could not find specified class loader: "
+                                + classLoaderName, e);
+            }
+        }
+        return classLoader;
+    }
+
+    /**
+     * Send notification to client's application. Used to notify client of
+     * critical errors and session expiration due to long inactivity. Server has
+     * no knowledge of what application client refers to.
+     * 
+     * @param request
+     *            the HTTP request instance.
+     * @param response
+     *            the HTTP response to write to.
+     * @param caption
+     *            for the notification
+     * @param message
+     *            for the notification
+     * @param url
+     *            url to load after message, null for current page
+     * @throws IOException
+     *             if the writing failed due to input/output error.
+     */
+    void criticalNotification(HttpServletRequest request,
+            HttpServletResponse response, String caption, String message,
+            String url) throws IOException {
+
+        // clients JS app is still running, but server application either
+        // no longer exists or it might fail to perform reasonably.
+        // send a notification to client's application and link how
+        // to "restart" application.
+
+        if (caption != null) {
+            caption = "\"" + caption + "\"";
+        }
+        if (message != null) {
+            message = "\"" + message + "\"";
+        }
+        if (url != null) {
+            url = "\"" + url + "\"";
+        }
+
+        // Set the response type
+        response.setContentType("application/json; charset=UTF-8");
+        final ServletOutputStream out = response.getOutputStream();
+        final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
+                new OutputStreamWriter(out, "UTF-8")));
+        outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {"
+                + "\"appError\": {" + "\"caption\":" + caption + ","
+                + "\"message\" : " + message + "," + "\"url\" : " + url
+                + "}}, \"resources\": {}, \"locales\":[]}]");
+        outWriter.flush();
+        outWriter.close();
+        out.flush();
+    }
+
+    /**
+     * Returns the application instance to be used for the request. If an
+     * existing instance is not found a new one is created or null is returned
+     * to indicate that the application is not available.
+     * 
+     * @param request
+     * @param requestType
+     * @return
+     * @throws MalformedURLException
+     * @throws SAXException
+     * @throws IllegalAccessException
+     * @throws InstantiationException
+     * @throws ServletException
+     * @throws SessionExpired
+     */
+    private Application findApplicationInstance(HttpServletRequest request,
+            RequestType requestType) throws MalformedURLException,
+            SAXException, IllegalAccessException, InstantiationException,
+            ServletException, SessionExpired {
+
+        final boolean restartApplication = (request
+                .getParameter(URL_PARAMETER_RESTART_APPLICATION) != null);
+        final boolean closeApplication = (request
+                .getParameter(URL_PARAMETER_CLOSE_APPLICATION) != null);
+
+        Application application = getExistingApplication(request);
+        if (application != null) {
+            /*
+             * There is an existing application. We can use this as long as the
+             * user not specifically requested to close or restart it.
+             */
+            if (restartApplication) {
+                closeApplication(application, request.getSession(false));
+                return createApplication(request);
+            } else if (closeApplication) {
+                closeApplication(application, request.getSession(false));
+                return null;
+            } else {
+                return application;
+            }
+        }
+
+        // No existing application was found
+        if (requestType == RequestType.UIDL && isRepaintAll(request)) {
+            /*
+             * UIDL request contains valid repaintAll=1 event, the user probably
+             * wants to initiate a new application through a custom index.html
+             * without using writeAjaxPage.
+             */
+            return createApplication(request);
+
+        } else if (requestType == RequestType.OTHER) {
+            /*
+             * TODO Should *any* request really create a new application
+             * instance if none was found?
+             */
+            return createApplication(request);
+
+        } else {
+            /* The application was not found. Assume the session has expired. */
+            throw new SessionExpired();
+        }
+
+    }
+
+    /**
+     * Gets resource path using different implementations. Required to
+     * supporting different servlet container implementations (application
+     * servers).
+     * 
+     * @param servletContext
+     * @param path
+     *            the resource path.
+     * @return the resource path.
+     */
+    protected static String getResourcePath(ServletContext servletContext,
+            String path) {
+        String resultPath = null;
+        resultPath = servletContext.getRealPath(path);
+        if (resultPath != null) {
+            return resultPath;
+        } else {
+            try {
+                final URL url = servletContext.getResource(path);
+                resultPath = url.getFile();
+            } catch (final Exception e) {
+                // FIXME: Handle exception
+                e.printStackTrace();
+            }
+        }
+        return resultPath;
+    }
+
+    /**
+     * Handles the requested URI. An application can add handlers to do special
+     * processing, when a certain URI is requested. The handlers are invoked
+     * before any windows URIs are processed and if a DownloadStream is returned
+     * it is sent to the client.
+     * 
+     * @param stream
+     *            the download stream.
+     * 
+     * @param request
+     *            the HTTP request instance.
+     * @param response
+     *            the HTTP response to write to.
+     * @throws IOException
+     * 
+     * @see com.itmill.toolkit.terminal.URIHandler
+     */
+    private void handleDownload(DownloadStream stream,
+            HttpServletRequest request, HttpServletResponse response)
+            throws IOException {
+
+        if (stream.getParameter("Location") != null) {
+            response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+            response.addHeader("Location", stream.getParameter("Location"));
+            return;
+        }
+
+        // Download from given stream
+        final InputStream data = stream.getStream();
+        if (data != null) {
+
+            // Sets content type
+            response.setContentType(stream.getContentType());
+
+            // Sets cache headers
+            final long cacheTime = stream.getCacheTime();
+            if (cacheTime <= 0) {
+                response.setHeader("Cache-Control", "no-cache");
+                response.setHeader("Pragma", "no-cache");
+                response.setDateHeader("Expires", 0);
+            } else {
+                response.setHeader("Cache-Control", "max-age=" + cacheTime
+                        / 1000);
+                response.setDateHeader("Expires", System.currentTimeMillis()
+                        + cacheTime);
+                response.setHeader("Pragma", "cache"); // Required to apply
+                // caching in some
+                // Tomcats
+            }
+
+            // Copy download stream parameters directly
+            // to HTTP headers.
+            final Iterator i = stream.getParameterNames();
+            if (i != null) {
+                while (i.hasNext()) {
+                    final String param = (String) i.next();
+                    response.setHeader(param, stream.getParameter(param));
+                }
+            }
+
+            // suggest local filename from DownloadStream if Content-Disposition
+            // not explicitly set
+            String contentDispositionValue = stream
+                    .getParameter("Content-Disposition");
+            if (contentDispositionValue == null) {
+                contentDispositionValue = "filename=\"" + stream.getFileName()
+                        + "\"";
+                response.setHeader("Content-Disposition",
+                        contentDispositionValue);
+            }
+
+            int bufferSize = stream.getBufferSize();
+            if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) {
+                bufferSize = DEFAULT_BUFFER_SIZE;
+            }
+            final byte[] buffer = new byte[bufferSize];
+            int bytesRead = 0;
+
+            final OutputStream out = response.getOutputStream();
+
+            while ((bytesRead = data.read(buffer)) > 0) {
+                out.write(buffer, 0, bytesRead);
+                out.flush();
+            }
+            out.close();
+
+        }
+
+    }
+
+    /**
+     * Creates and starts a new application. This is not meant to be overridden.
+     * Override getNewApplication to create the application instance in a custom
+     * way.
+     * 
+     * @param request
+     * @return
+     * @throws ServletException
+     * @throws MalformedURLException
+     */
+    private Application createApplication(HttpServletRequest request)
+            throws ServletException, MalformedURLException {
+        Application newApplication = getNewApplication(request);
+
+        // Create application
+        final URL applicationUrl = getApplicationUrl(request);
+
+        // Initial locale comes from the request
+        Locale locale = request.getLocale();
+        HttpSession session = request.getSession();
+
+        // Start the newly created application
+        startApplication(session, newApplication, applicationUrl, locale);
+
+        return newApplication;
+    }
+
+    private void handleServiceException(HttpServletRequest request,
+            HttpServletResponse response, Application application, Throwable e)
+            throws IOException, ServletException {
+        // if this was an UIDL request, response UIDL back to client
+        if (getRequestType(request) == RequestType.UIDL) {
+            Application.SystemMessages ci = getSystemMessages();
+            criticalNotification(request, response, ci
+                    .getInternalErrorCaption(), ci.getInternalErrorMessage(),
+                    ci.getInternalErrorURL());
+            if (application != null) {
+                application.getErrorHandler()
+                        .terminalError(new RequestError(e));
+            } else {
+                throw new ServletException(e);
+            }
+        } else {
+            // Re-throw other exceptions
+            throw new ServletException(e);
+        }
+
+    }
+
+    private String getThemeForWindow(HttpServletRequest request, Window window) {
+        // Finds theme name
+        String themeName = window.getTheme();
+        if (request.getParameter(URL_PARAMETER_THEME) != null) {
+            themeName = request.getParameter(URL_PARAMETER_THEME);
+        }
+
+        if (themeName == null) {
+            themeName = "default";
+        }
+
+        return themeName;
+    }
+
+    /**
+     * Calls URI handlers for the request. If an URI handler returns a
+     * DownloadStream the stream is passed to the client for downloading.
+     * 
+     * @param applicationManager
+     * @param window
+     * @param request
+     * @param response
+     * @return true if an DownloadStream was sent to the client, false otherwise
+     * @throws IOException
+     */
+    private boolean handleURI(CommunicationManager applicationManager,
+            Window window, HttpServletRequest request,
+            HttpServletResponse response) throws IOException {
+        // Handles the URI
+        DownloadStream download = applicationManager.handleURI(window, request,
+                response);
+
+        // A download request
+        if (download != null) {
+            // Client downloads an resource
+            handleDownload(download, request, response);
+            return true;
+        }
+
+        return false;
+    }
+
+    private void handleServiceSessionExpired(HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException {
+
+        if (isOnUnloadRequest(request)) {
+            /*
+             * Request was an unload request (e.g. window close event) and the
+             * client expects no response if it fails.
+             */
+            return;
+        }
+
+        try {
+            Application.SystemMessages ci = getSystemMessages();
+            if (getRequestType(request) != RequestType.UIDL) {
+                // 'plain' http req - e.g. browser reload;
+                // just go ahead redirect the browser
+                response.sendRedirect(ci.getSessionExpiredURL());
+            } else {
+                // send uidl redirect
+                criticalNotification(request, response, ci
+                        .getSessionExpiredCaption(), ci
+                        .getSessionExpiredMessage(), ci.getSessionExpiredURL());
+                /*
+                 * Invalidate session (weird to have session if we're saying
+                 * that it's expired, and worse: portal integration will fail
+                 * since the session is not created by the portal.
+                 */
+                request.getSession().invalidate();
+            }
+        } catch (SystemMessageException ee) {
+            throw new ServletException(ee);
+        }
+
+    }
+
+    private void handleServiceSecurityException(HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException {
+        if (isOnUnloadRequest(request)) {
+            /*
+             * Request was an unload request (e.g. window close event) and the
+             * client expects no response if it fails.
+             */
+            return;
+        }
+
+        // TODO handle differently?
+        // Invalid security key, show session expired message for now.
+        handleServiceSessionExpired(request, response);
+    }
+
+    /**
+     * Creates a new application for the given request.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @return A new Application instance.
+     * @throws ServletException
+     */
+    protected abstract Application getNewApplication(HttpServletRequest request)
+            throws ServletException;
+
+    /**
+     * Starts the application if it is not already running. Ensures the
+     * application is added to the WebApplicationContext.
+     * 
+     * @param session
+     * @param application
+     * @param applicationUrl
+     * @param locale
+     * @throws ServletException
+     */
+    private void startApplication(HttpSession session, Application application,
+            URL applicationUrl, Locale locale) throws ServletException {
+        if (application == null) {
+            throw new ServletException(
+                    "Application is null and can't be started");
+        }
+
+        if (!application.isRunning()) {
+            final WebApplicationContext context = WebApplicationContext
+                    .getApplicationContext(session);
+            // final URL applicationUrl = getApplicationUrl(request);
+            context.addApplication(application);
+            application.setLocale(locale);
+            application.start(applicationUrl, applicationProperties, context);
+        }
+    }
+
+    /**
+     * Check if this is a request for a static resource and, if it is, serve the
+     * resource to the client. Returns true if a file was served and the request
+     * has been handled, false otherwise.
+     * 
+     * @param request
+     * @param response
+     * @return
+     * @throws IOException
+     * @throws ServletException
+     */
+    private boolean serveStaticResources(HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException {
+
+        // FIXME What does 10 refer to?
+        String pathInfo = request.getPathInfo();
+        if (pathInfo == null || pathInfo.length() <= 10) {
+            return false;
+        }
+
+        if ((request.getContextPath() != null)
+                && (request.getRequestURI().startsWith("/ITMILL/"))) {
+            serveStaticResourcesInITMILL(request.getRequestURI(), response);
+            return true;
+        } else if (request.getRequestURI().startsWith(
+                request.getContextPath() + "/ITMILL/")) {
+            serveStaticResourcesInITMILL(request.getRequestURI().substring(
+                    request.getContextPath().length()), response);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Serve resources from ITMILL directory.
+     * 
+     * @param request
+     * @param response
+     * @throws IOException
+     * @throws ServletException
+     */
+    private void serveStaticResourcesInITMILL(String filename,
+            HttpServletResponse response) throws IOException, ServletException {
+
+        final ServletContext sc = getServletContext();
+        InputStream is = sc.getResourceAsStream(filename);
+        if (is == null) {
+            // try if requested file is found from classloader
+
+            // strip leading "/" otherwise stream from JAR wont work
+            filename = filename.substring(1);
+            is = getClassLoader().getResourceAsStream(filename);
+
+            if (is == null) {
+                // cannot serve requested file
+                System.err
+                        .println("Requested resource ["
+                                + filename
+                                + "] not found from filesystem or through class loader."
+                                + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/ITMILL folder.");
+                response.setStatus(404);
+                return;
+            }
+        }
+        final String mimetype = sc.getMimeType(filename);
+        if (mimetype != null) {
+            response.setContentType(mimetype);
+        }
+        final OutputStream os = response.getOutputStream();
+        final byte buffer[] = new byte[20000];
+        int bytes;
+        while ((bytes = is.read(buffer)) >= 0) {
+            os.write(buffer, 0, bytes);
+        }
+    }
+
+    private enum RequestType {
+        FILE_UPLOAD, UIDL, OTHER;
+    }
+
+    private RequestType getRequestType(HttpServletRequest request) {
+        if (isFileUploadRequest(request)) {
+            return RequestType.FILE_UPLOAD;
+        } else if (isUIDLRequest(request)) {
+            return RequestType.UIDL;
+        }
+
+        return RequestType.OTHER;
+    }
+
+    private boolean isUIDLRequest(HttpServletRequest request) {
+        String pathInfo = getRequestPathInfo(request);
+
+        if (pathInfo == null) {
+            return false;
+        }
+
+        String compare = AJAX_UIDL_URI;
+
+        if (pathInfo.startsWith(compare + "/") || pathInfo.endsWith(compare)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean isFileUploadRequest(HttpServletRequest request) {
+        return ServletFileUpload.isMultipartContent(request);
+    }
+
+    private boolean isOnUnloadRequest(HttpServletRequest request) {
+        return request.getParameter(ApplicationConnection.PARAM_UNLOADBURST) != null;
+    }
+
+    /**
+     * Get system messages from the current application class
+     * 
+     * @return
+     */
+    protected SystemMessages getSystemMessages() {
+        try {
+            Class appCls = getApplicationClass();
+            Method m = appCls.getMethod("getSystemMessages", (Class[]) null);
+            return (Application.SystemMessages) m.invoke(null, (Object[]) null);
+        } catch (ClassNotFoundException e) {
+            // This should never happen
+            throw new SystemMessageException(e);
+        } catch (SecurityException e) {
+            throw new SystemMessageException(
+                    "Application.getSystemMessage() should be static public", e);
+        } catch (NoSuchMethodException e) {
+            // This is completely ok and should be silently ignored
+        } catch (IllegalArgumentException e) {
+            // This should never happen
+            throw new SystemMessageException(e);
+        } catch (IllegalAccessException e) {
+            throw new SystemMessageException(
+                    "Application.getSystemMessage() should be static public", e);
+        } catch (InvocationTargetException e) {
+            // This should never happen
+            throw new SystemMessageException(e);
+        }
+        return Application.getSystemMessages();
+    }
+
+    protected abstract Class getApplicationClass()
+            throws ClassNotFoundException;
+
+    /**
+     * Resolve widgetset URL. Widgetset is not application specific.
+     * 
+     * @param request
+     * @return
+     * @throws MalformedURLException
+     */
+    String getWidgetsetLocation(HttpServletRequest request)
+            throws MalformedURLException {
+        URL applicationURL = getApplicationUrl(request);
+
+        String applicationPath = applicationURL.getPath();
+        String pathParts[] = applicationPath.split("\\/");
+
+        // if context is specified add it to widgetsetUrl
+        String ctxPath = request.getContextPath();
+        if (ctxPath.length() == 0
+                && request.getAttribute("javax.servlet.include.context_path") != null) {
+            // include request (e.g portlet), get context path from
+            // attribute
+            ctxPath = (String) request
+                    .getAttribute("javax.servlet.include.context_path");
+        }
+
+        String widgetsetPath = "";
+        if (pathParts.length > 1
+                && pathParts[1].equals(ctxPath.replaceAll("\\/", ""))) {
+            widgetsetPath = "/" + pathParts[1];
+        }
+
+        return widgetsetPath;
+
+    }
+
+    /**
+     * 
+     * @param request
+     *            the HTTP request.
+     * @param response
+     *            the HTTP response to write to.
+     * @param out
+     * @param unhandledParameters
+     * @param window
+     * @param terminalType
+     * @param theme
+     * @throws IOException
+     *             if the writing failed due to input/output error.
+     * @throws MalformedURLException
+     *             if the application is denied access the persistent data store
+     *             represented by the given URL.
+     */
+    private void writeAjaxPage(HttpServletRequest request,
+            HttpServletResponse response, Window window, String themeName,
+            Application application) throws IOException, MalformedURLException,
+            ServletException {
+
+        // e.g portlets only want a html fragment
+        boolean fragment = (request.getAttribute(REQUEST_FRAGMENT) != null);
+        if (fragment) {
+            request.setAttribute(Application.class.getName(), application);
+        }
+
+        final BufferedWriter page = new BufferedWriter(new OutputStreamWriter(
+                response.getOutputStream()));
+        String pathInfo = getRequestPathInfo(request);
+        if (pathInfo == null) {
+            pathInfo = "/";
+        }
+
+        String title = ((window.getCaption() == null) ? "IT Mill Toolkit 5"
+                : window.getCaption());
+
+        String widgetset = null;
+        // request widgetset takes precedence (e.g portlet include)
+        Object reqParam = request.getAttribute(REQUEST_WIDGETSET);
+        try {
+            widgetset = (String) reqParam;
+        } catch (Exception e) {
+            // FIXME: Handle exception
+            System.err.println("Warning: request param '" + REQUEST_WIDGETSET
+                    + "' could not be used (is not a String)" + e);
+        }
+
+        // TODO: Any reason this could not use
+        // getApplicationOrSystemProperty with DEFAULT_WIDGETSET as default
+        // value ?
+        if (widgetset == null) {
+            widgetset = getApplicationProperty(PARAMETER_WIDGETSET);
+        }
+        if (widgetset == null) {
+            widgetset = DEFAULT_WIDGETSET;
+        }
+
+        /* Fetch relative url to application */
+        // don't use server and port in uri. It may cause problems with some
+        // virtual server configurations which lose the server name
+        String appUrl = getApplicationUrl(request).getPath();
+        if (appUrl.endsWith("/")) {
+            appUrl = appUrl.substring(0, appUrl.length() - 1);
+        }
+
+        final String widgetsetPath = getWidgetsetLocation(request);
+
+        final String staticFilePath = getApplicationOrSystemProperty(
+                PARAMETER_ITMILL_RESOURCES, widgetsetPath);
+
+        // Default theme does not use theme URI
+        String themeUri = null;
+        if (themeName != null) {
+            // Using custom theme
+            themeUri = staticFilePath + "/" + THEME_DIRECTORY_PATH + themeName;
+        }
+
+        if (!fragment) {
+            // Window renders are not cacheable
+            response.setHeader("Cache-Control", "no-cache");
+            response.setHeader("Pragma", "no-cache");
+            response.setDateHeader("Expires", 0);
+            response.setContentType("text/html; charset=UTF-8");
+
+            // write html header
+            page.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD "
+                    + "XHTML 1.0 Transitional//EN\" "
+                    + "\"http://www.w3.org/TR/xhtml1/"
+                    + "DTD/xhtml1-transitional.dtd\">\n");
+
+            page.write("<html xmlns=\"http://www.w3.org/1999/xhtml\""
+                    + ">\n<head>\n");
+            page
+                    .write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n");
+            page
+                    .write("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=7\" />\n");
+            page.write("<style type=\"text/css\">"
+                    + "html, body {height:100%;}</style>");
+
+            // Add favicon links
+            page
+                    .write("<link rel=\"shortcut icon\" type=\"image/vnd.microsoft.icon\" href=\""
+                            + themeUri + "/favicon.ico\" />");
+            page
+                    .write("<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\""
+                            + themeUri + "/favicon.ico\" />");
+
+            page.write("<title>" + title + "</title>");
+
+            page
+                    .write("\n</head>\n<body scroll=\"auto\" class=\"i-generated-body\">\n");
+        }
+
+        String appId = appUrl;
+        if ("".equals(appUrl)) {
+            appId = "ROOT";
+        }
+        appId = appId.replaceAll("[^a-zA-Z0-9]", "");
+        // Add hashCode to the end, so that it is still (sort of) predictable,
+        // but indicates that it should not be used in CSS and such:
+        int hashCode = appId.hashCode();
+        if (hashCode < 0) {
+            hashCode = -hashCode;
+        }
+        appId = appId + "-" + hashCode;
+
+        // Get system messages
+        Application.SystemMessages systemMessages = null;
+        try {
+            systemMessages = getSystemMessages();
+        } catch (SystemMessageException e) {
+            // failing to get the system messages is always a problem
+            throw new ServletException("CommunicationError!", e);
+        }
+
+        if (isGecko17(request)) {
+            // special start page for gecko 1.7 versions. Firefox 1.0 is not
+            // supported, but the hack is make it possible to use linux and
+            // hosted mode browser for debugging. Note that due this hack,
+            // debugging gwt code in portals with linux will be problematic if
+            // there are multiple toolkit portlets visible at the same time.
+            // TODO remove this when hosted mode on linux gets newer gecko
+
+            page.write("<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
+                    + "style=\"width:0;height:0;border:0;overflow:"
+                    + "hidden\" src=\"javascript:false\"></iframe>\n");
+            page.write("<script language='javascript' src='" + staticFilePath
+                    + "/" + WIDGETSET_DIRECTORY_PATH + widgetset + "/"
+                    + widgetset + ".nocache.js?" + new Date().getTime()
+                    + "'></script>\n");
+            page.write("<script type=\"text/javascript\">\n");
+            page.write("//<![CDATA[\n");
+            page.write("if(!itmill || !itmill.toolkitConfigurations) {\n "
+                    + "if(!itmill) { var itmill = {}} \n"
+                    + "itmill.toolkitConfigurations = {};\n"
+                    + "itmill.themesLoaded = {}};\n");
+
+            if (!isProductionMode()) {
+                page.write("itmill.debug = true;\n");
+            }
+
+            page.write("itmill.toolkitConfigurations[\"" + appId + "\"] = {");
+            page.write("appUri:'" + appUrl + "', ");
+            page.write("pathInfo: '" + pathInfo + "', ");
+            if (window != application.getMainWindow()) {
+                page.write("windowName: '" + window.getName() + "', ");
+            }
+            page.write("themeUri:");
+            page.write(themeUri != null ? "'" + themeUri + "'" : "null");
+            page.write(", versionInfo : {toolkitVersion:\"");
+            page.write(VERSION);
+            page.write("\",applicationVersion:\"");
+            page.write(application.getVersion());
+            page.write("\"},");
+            if (systemMessages != null) {
+                // Write the CommunicationError -message to client
+                String caption = systemMessages.getCommunicationErrorCaption();
+                if (caption != null) {
+                    caption = "\"" + caption + "\"";
+                }
+                String message = systemMessages.getCommunicationErrorMessage();
+                if (message != null) {
+                    message = "\"" + message + "\"";
+                }
+                String url = systemMessages.getCommunicationErrorURL();
+                if (url != null) {
+                    url = "\"" + url + "\"";
+                }
+                page.write("\"comErrMsg\": {" + "\"caption\":" + caption + ","
+                        + "\"message\" : " + message + "," + "\"url\" : " + url
+                        + "}");
+            }
+            page.write("};\n//]]>\n</script>\n");
+
+            if (themeName != null) {
+                // Custom theme's stylesheet, load only once, in different
+                // script
+                // tag to be dominate styles injected by widget
+                // set
+                page.write("<script type=\"text/javascript\">\n");
+                page.write("//<![CDATA[\n");
+                page.write("if(!itmill.themesLoaded['" + themeName + "']) {\n");
+                page
+                        .write("var stylesheet = document.createElement('link');\n");
+                page.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
+                page.write("stylesheet.setAttribute('type', 'text/css');\n");
+                page.write("stylesheet.setAttribute('href', '" + themeUri
+                        + "/styles.css');\n");
+                page
+                        .write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
+                page.write("itmill.themesLoaded['" + themeName
+                        + "'] = true;\n}\n");
+                page.write("//]]>\n</script>\n");
+            }
+
+        } else {
+            page.write("<script type=\"text/javascript\">\n");
+            page.write("//<![CDATA[\n");
+            page.write("if(!itmill || !itmill.toolkitConfigurations) {\n "
+                    + "if(!itmill) { var itmill = {}} \n"
+                    + "itmill.toolkitConfigurations = {};\n"
+                    + "itmill.themesLoaded = {};\n");
+            if (!isProductionMode()) {
+                page.write("itmill.debug = true;\n");
+            }
+            page
+                    .write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
+                            + "style=\"width:0;height:0;border:0;overflow:"
+                            + "hidden\" src=\"javascript:false\"></iframe>');\n");
+            page.write("document.write(\"<script language='javascript' src='"
+                    + staticFilePath + "/" + WIDGETSET_DIRECTORY_PATH
+                    + widgetset + "/" + widgetset + ".nocache.js?"
+                    + new Date().getTime() + "'><\\/script>\");\n}\n");
+
+            page.write("itmill.toolkitConfigurations[\"" + appId + "\"] = {");
+            page.write("appUri:'" + appUrl + "', ");
+            page.write("pathInfo: '" + pathInfo + "', ");
+            if (window != application.getMainWindow()) {
+                page.write("windowName: '" + window.getName() + "', ");
+            }
+            page.write("themeUri:");
+            page.write(themeUri != null ? "'" + themeUri + "'" : "null");
+            page.write(", versionInfo : {toolkitVersion:\"");
+            page.write(VERSION);
+            page.write("\",applicationVersion:\"");
+            page.write(application.getVersion());
+            page.write("\"},");
+            if (systemMessages != null) {
+                // Write the CommunicationError -message to client
+                String caption = systemMessages.getCommunicationErrorCaption();
+                if (caption != null) {
+                    caption = "\"" + caption + "\"";
+                }
+                String message = systemMessages.getCommunicationErrorMessage();
+                if (message != null) {
+                    message = "\"" + message + "\"";
+                }
+                String url = systemMessages.getCommunicationErrorURL();
+                if (url != null) {
+                    url = "\"" + url + "\"";
+                }
+
+                page.write("\"comErrMsg\": {" + "\"caption\":" + caption + ","
+                        + "\"message\" : " + message + "," + "\"url\" : " + url
+                        + "}");
+            }
+            page.write("};\n//]]>\n</script>\n");
+
+            if (themeName != null) {
+                // Custom theme's stylesheet, load only once, in different
+                // script
+                // tag to be dominate styles injected by widget
+                // set
+                page.write("<script type=\"text/javascript\">\n");
+                page.write("//<![CDATA[\n");
+                page.write("if(!itmill.themesLoaded['" + themeName + "']) {\n");
+                page
+                        .write("var stylesheet = document.createElement('link');\n");
+                page.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
+                page.write("stylesheet.setAttribute('type', 'text/css');\n");
+                page.write("stylesheet.setAttribute('href', '" + themeUri
+                        + "/styles.css');\n");
+                page
+                        .write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
+                page.write("itmill.themesLoaded['" + themeName
+                        + "'] = true;\n}\n");
+                page.write("//]]>\n</script>\n");
+            }
+        }
+
+        // Warn if the widgetset has not been loaded after 15 seconds on
+        // inactivity
+        page.write("<script type=\"text/javascript\">\n");
+        page.write("//<![CDATA[\n");
+        page.write("setTimeout('if (typeof " + widgetset.replace('.', '_')
+                + " == \"undefined\") {alert(\"Failed to load the widgetset: "
+                + staticFilePath + "/" + WIDGETSET_DIRECTORY_PATH + widgetset
+                + "/" + widgetset + ".nocache.js\")};',15000);\n"
+                + "//]]>\n</script>\n");
+
+        String style = null;
+        reqParam = request.getAttribute(REQUEST_APPSTYLE);
+        if (reqParam != null) {
+            style = "style=\"" + reqParam + "\"";
+        }
+        /*- Add classnames; 
+         *      .i-app 
+         *      .i-app-loading
+         *      .i-app-<simpleName for app class> 
+         *      .i-theme-<themeName, remove non-alphanum>
+         */
+        String appClass = "i-app-";
+        try {
+            appClass += getApplicationClass().getSimpleName();
+        } catch (ClassNotFoundException e) {
+            appClass += "unknown";
+
+            e.printStackTrace();
+        }
+        String themeClass = "";
+        if (themeName != null) {
+            themeClass = "i-theme-" + themeName.replaceAll("[^a-zA-Z0-9]", "");
+        }
+
+        page.write("<div id=\"" + appId + "\" class=\"i-app i-app-loading "
+                + themeClass + " " + appClass + "\" "
+                + (style != null ? style : "") + "></div>\n");
+
+        if (!fragment) {
+            page.write("<noscript>" + getNoScriptMessage() + "</noscript>");
+            page.write("</body>\n</html>\n");
+        }
+        page.close();
+
+    }
+
+    /**
+     * Returns a message printed for browsers without scripting support or if
+     * browsers scripting support is disabled.
+     */
+    protected String getNoScriptMessage() {
+        return "You have to enable javascript in your browser to use an application built with IT Mill Toolkit.";
+    }
+
+    private boolean isGecko17(HttpServletRequest request) {
+        final WebBrowser browser = WebApplicationContext.getApplicationContext(
+                request.getSession()).getBrowser();
+        if (browser != null && browser.getBrowserApplication() != null) {
+            if (browser.getBrowserApplication().indexOf("rv:1.7.") > 0
+                    && browser.getBrowserApplication().indexOf("Gecko") > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Handles theme resource file requests. Resources supplied with the themes
+     * are provided by the WebAdapterServlet.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @param response
+     *            the HTTP response.
+     * @return boolean <code>true</code> if the request was handled and further
+     *         processing should be suppressed, <code>false</code> otherwise.
+     * @throws ServletException
+     *             if an exception has occurred that interferes with the
+     *             servlet's normal operation.
+     */
+    private boolean handleResourceRequest(HttpServletRequest request,
+            HttpServletResponse response, String themeName)
+            throws ServletException {
+
+        // If the resource path is unassigned, initialize it
+        if (resourcePath == null) {
+            resourcePath = request.getContextPath() + request.getServletPath()
+                    + RESOURCE_URI;
+            // WebSphere Application Server related fix
+            resourcePath = resourcePath.replaceAll("//", "/");
+        }
+
+        String resourceId = request.getPathInfo();
+
+        // Checks if this really is a resource request
+        if (resourceId == null || !resourceId.startsWith(RESOURCE_URI)) {
+            return false;
+        }
+
+        // Checks the resource type
+        resourceId = resourceId.substring(RESOURCE_URI.length());
+        InputStream data = null;
+
+        // Gets theme resources
+        try {
+            data = getServletContext().getResourceAsStream(
+                    THEME_DIRECTORY_PATH + themeName + "/" + resourceId);
+        } catch (final Exception e) {
+            // FIXME: Handle exception
+            e.printStackTrace();
+            data = null;
+        }
+
+        // Writes the response
+        try {
+            if (data != null) {
+                response.setContentType(FileTypeResolver
+                        .getMIMEType(resourceId));
+
+                // Use default cache time for theme resources
+                response.setHeader("Cache-Control", "max-age="
+                        + DEFAULT_THEME_CACHETIME / 1000);
+                response.setDateHeader("Expires", System.currentTimeMillis()
+                        + DEFAULT_THEME_CACHETIME);
+                response.setHeader("Pragma", "cache"); // Required to apply
+                // caching in some
+                // Tomcats
+
+                // Writes the data to client
+                final byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+                int bytesRead = 0;
+                final OutputStream out = response.getOutputStream();
+                while ((bytesRead = data.read(buffer)) > 0) {
+                    out.write(buffer, 0, bytesRead);
+                }
+                out.close();
+                data.close();
+            } else {
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            }
+
+        } catch (final java.io.IOException e) {
+            // FIXME: Handle exception
+            System.err.println("Resource transfer failed:  "
+                    + request.getRequestURI() + ". (" + e.getMessage() + ")");
+        }
+
+        return true;
+    }
+
+    /**
+     * Gets the current application URL from request.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @throws MalformedURLException
+     *             if the application is denied access to the persistent data
+     *             store represented by the given URL.
+     */
+    URL getApplicationUrl(HttpServletRequest request)
+            throws MalformedURLException {
+        final URL reqURL = new URL(
+                (request.isSecure() ? "https://" : "http://")
+                        + request.getServerName()
+                        + ((request.isSecure() && request.getServerPort() == 443)
+                                || (!request.isSecure() && request
+                                        .getServerPort() == 80) ? "" : ":"
+                                + request.getServerPort())
+                        + request.getRequestURI());
+        String servletPath = "";
+        if (request.getAttribute("javax.servlet.include.servlet_path") != null) {
+            // this is an include request
+            servletPath = request.getAttribute(
+                    "javax.servlet.include.context_path").toString()
+                    + request
+                            .getAttribute("javax.servlet.include.servlet_path");
+
+        } else {
+            servletPath = request.getContextPath() + request.getServletPath();
+        }
+
+        if (servletPath.length() == 0
+                || servletPath.charAt(servletPath.length() - 1) != '/') {
+            servletPath = servletPath + "/";
+        }
+
+        URL u = new URL(reqURL, servletPath);
+        return u;
+    }
+
+    /**
+     * Gets the existing application for given request. Looks for application
+     * instance for given request based on the requested URL.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @return Application instance, or null if the URL does not map to valid
+     *         application.
+     * @throws MalformedURLException
+     *             if the application is denied access to the persistent data
+     *             store represented by the given URL.
+     * @throws SAXException
+     * @throws IllegalAccessException
+     * @throws InstantiationException
+     */
+    private Application getExistingApplication(HttpServletRequest request)
+            throws MalformedURLException, SAXException, IllegalAccessException,
+            InstantiationException {
+
+        // Ensures that the session is still valid
+        final HttpSession session = request.getSession(true);
+
+        WebApplicationContext context = WebApplicationContext
+                .getApplicationContext(session);
+
+        // Gets application list for the session.
+        final Collection applications = context.getApplications();
+
+        // Search for the application (using the application URI) from the list
+        for (final Iterator i = applications.iterator(); i.hasNext();) {
+            final Application sessionApplication = (Application) i.next();
+            final String sessionApplicationPath = sessionApplication.getURL()
+                    .getPath();
+            String requestApplicationPath = getApplicationUrl(request)
+                    .getPath();
+
+            if (requestApplicationPath.equals(sessionApplicationPath)) {
+                // Found a running application
+                if (sessionApplication.isRunning()) {
+                    return sessionApplication;
+                }
+                // Application has stopped, so remove it before creating a new
+                // application
+                WebApplicationContext.getApplicationContext(session)
+                        .removeApplication(sessionApplication);
+                break;
+            }
+        }
+
+        // Existing application not found
+        return null;
+    }
+
+    /**
+     * Ends the application.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @param response
+     *            the HTTP response to write to.
+     * @param application
+     *            the application to end.
+     * @throws IOException
+     *             if the writing failed due to input/output error.
+     */
+    private void endApplication(HttpServletRequest request,
+            HttpServletResponse response, Application application)
+            throws IOException {
+
+        String logoutUrl = application.getLogoutURL();
+        if (logoutUrl == null) {
+            logoutUrl = application.getURL().toString();
+        }
+
+        final HttpSession session = request.getSession();
+        if (session != null) {
+            WebApplicationContext.getApplicationContext(session)
+                    .removeApplication(application);
+        }
+
+        response.sendRedirect(response.encodeRedirectURL(logoutUrl));
+    }
+
+    /**
+     * Gets the existing application or create a new one. Get a window within an
+     * application based on the requested URI.
+     * 
+     * @param request
+     *            the HTTP Request.
+     * @param application
+     *            the Application to query for window.
+     * @return Window matching the given URI or null if not found.
+     * @throws ServletException
+     *             if an exception has occurred that interferes with the
+     *             servlet's normal operation.
+     */
+    private Window getApplicationWindow(HttpServletRequest request,
+            Application application) throws ServletException {
+
+        Window window = null;
+
+        // Finds the window where the request is handled
+        String path = getRequestPathInfo(request);
+
+        // Main window as the URI is empty
+        if (path == null || path.length() == 0 || path.equals("/")
+                || path.startsWith("/APP/")) {
+            window = application.getMainWindow();
+        } else {
+            String windowName = null;
+            if (path.charAt(0) == '/') {
+                path = path.substring(1);
+            }
+            final int index = path.indexOf('/');
+            if (index < 0) {
+                windowName = path;
+                path = "";
+            } else {
+                windowName = path.substring(0, index);
+                path = path.substring(index + 1);
+            }
+            window = application.getWindow(windowName);
+
+            if (window == null) {
+                // By default, we use main window
+                window = application.getMainWindow();
+            } else if (!window.isVisible()) {
+                // Implicitly painting without actually invoking paint()
+                window.requestRepaintRequests();
+
+                // If the window is invisible send a blank page
+                return null;
+            }
+        }
+
+        return window;
+    }
+
+    String getRequestPathInfo(HttpServletRequest request) {
+        return request.getPathInfo();
+    }
+
+    /**
+     * Gets relative location of a theme resource.
+     * 
+     * @param theme
+     *            the Theme name.
+     * @param resource
+     *            the Theme resource.
+     * @return External URI specifying the resource
+     */
+    public String getResourceLocation(String theme, ThemeResource resource) {
+
+        if (resourcePath == null) {
+            return resource.getResourceId();
+        }
+        return resourcePath + theme + "/" + resource.getResourceId();
+    }
+
+    private boolean isRepaintAll(HttpServletRequest request) {
+        return (request.getParameter(URL_PARAMETER_REPAINT_ALL) != null)
+                && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1"));
+    }
+
+    private void closeApplication(Application application, HttpSession session) {
+        if (application == null) {
+            return;
+        }
+
+        application.close();
+        if (session != null) {
+            WebApplicationContext context = WebApplicationContext
+                    .getApplicationContext(session);
+            context.applicationToAjaxAppMgrMap.remove(application);
+            // FIXME: Move to WebApplicationContext
+            context.removeApplication(application);
+        }
+    }
+
+    /**
+     * Implementation of ParameterHandler.ErrorEvent interface.
+     */
+    public class ParameterHandlerErrorImpl implements
+            ParameterHandler.ErrorEvent, Serializable {
+
+        private ParameterHandler owner;
+
+        private Throwable throwable;
+
+        /**
+         * Gets the contained throwable.
+         * 
+         * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable()
+         */
+        public Throwable getThrowable() {
+            return throwable;
+        }
+
+        /**
+         * Gets the source ParameterHandler.
+         * 
+         * @see com.itmill.toolkit.terminal.ParameterHandler.ErrorEvent#getParameterHandler()
+         */
+        public ParameterHandler getParameterHandler() {
+            return owner;
+        }
+
+    }
+
+    /**
+     * Implementation of URIHandler.ErrorEvent interface.
+     */
+    public class URIHandlerErrorImpl implements URIHandler.ErrorEvent,
+            Serializable {
+
+        private final URIHandler owner;
+
+        private final Throwable throwable;
+
+        /**
+         * 
+         * @param owner
+         * @param throwable
+         */
+        private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) {
+            this.owner = owner;
+            this.throwable = throwable;
+        }
+
+        /**
+         * Gets the contained throwable.
+         * 
+         * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable()
+         */
+        public Throwable getThrowable() {
+            return throwable;
+        }
+
+        /**
+         * Gets the source URIHandler.
+         * 
+         * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler()
+         */
+        public URIHandler getURIHandler() {
+            return owner;
+        }
+    }
+
+    public class RequestError implements Terminal.ErrorEvent, Serializable {
+
+        private final Throwable throwable;
+
+        public RequestError(Throwable throwable) {
+            this.throwable = throwable;
+        }
+
+        public Throwable getThrowable() {
+            return throwable;
+        }
+
+    }
+}
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationRunnerServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationRunnerServlet.java
new file mode 100644 (file)
index 0000000..1932e0a
--- /dev/null
@@ -0,0 +1,173 @@
+package com.itmill.toolkit.terminal.gwt.server;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.itmill.toolkit.Application;
+
+@SuppressWarnings("serial")
+public class ApplicationRunnerServlet extends AbstractApplicationServlet {
+
+    /**
+     * The name of the application class currently used. Only valid within one
+     * request.
+     */
+    String applicationClassName = "";
+
+    @Override
+    public void init(ServletConfig servletConfig) throws ServletException {
+        super.init(servletConfig);
+    }
+
+    @Override
+    protected void service(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        applicationClassName = getApplicationRunnerApplicationClassName(request);
+        super.service(request, response);
+        applicationClassName = "";
+
+    }
+
+    @Override
+    URL getApplicationUrl(HttpServletRequest request)
+            throws MalformedURLException {
+        URL url = super.getApplicationUrl(request);
+
+        String path = url.toString();
+        path += applicationClassName;
+        path += "/";
+
+        return new URL(path);
+    }
+
+    @Override
+    protected Application getNewApplication(HttpServletRequest request)
+            throws ServletException {
+
+        // Creates a new application instance
+        try {
+            Class<?> applicationClass = getClassLoader().loadClass(
+                    applicationClassName);
+            final Application application = (Application) applicationClass
+                    .newInstance();
+
+            return application;
+        } catch (final IllegalAccessException e) {
+            throw new ServletException(e);
+        } catch (final InstantiationException e) {
+            throw new ServletException(e);
+        } catch (final ClassNotFoundException e) {
+            throw new ServletException(
+                    new InstantiationException(
+                            "Failed to load application class: "
+                                    + applicationClassName));
+        }
+
+    }
+
+    private String getApplicationRunnerApplicationClassName(
+            HttpServletRequest request) {
+        return getApplicationRunnerURIs(request).applicationClassname;
+    }
+
+    private static class URIS {
+        String widgetsetPath;
+        String applicationURI;
+        String context;
+        String runner;
+        String applicationClassname;
+
+    }
+
+    /**
+     * Parses application runner URIs.
+     * 
+     * If request URL is e.g.
+     * http://localhost:8080/itmill/run/com.itmill.toolkit.demo.Calc then
+     * <ul>
+     * <li>context=itmill</li>
+     * <li>Runner servlet=run</li>
+     * <li>Toolkit application=com.itmill.toolkit.demo.Calc</li>
+     * </ul>
+     * 
+     * @param request
+     * @return string array containing widgetset URI, application URI and
+     *         context, runner, application classname
+     */
+    private static URIS getApplicationRunnerURIs(HttpServletRequest request) {
+        final String[] urlParts = request.getRequestURI().toString().split(
+                "\\/");
+        String context = null;
+        String runner = null;
+        URIS uris = new URIS();
+        String applicationClassname = null;
+        String contextPath = request.getContextPath();
+        if (urlParts[1].equals(contextPath.replaceAll("\\/", ""))) {
+            // class name comes after web context and runner application
+            context = urlParts[1];
+            runner = urlParts[2];
+            if (urlParts.length == 3) {
+                throw new IllegalArgumentException("No application specified");
+            }
+            applicationClassname = urlParts[3];
+
+            uris.widgetsetPath = "/" + context;
+            uris.applicationURI = "/" + context + "/" + runner + "/"
+                    + applicationClassname;
+            uris.context = context;
+            uris.runner = runner;
+            uris.applicationClassname = applicationClassname;
+        } else {
+            // no context
+            context = "";
+            runner = urlParts[1];
+            if (urlParts.length == 2) {
+                throw new IllegalArgumentException("No application specified");
+            }
+            applicationClassname = urlParts[2];
+
+            uris.widgetsetPath = "/";
+            uris.applicationURI = "/" + runner + "/" + applicationClassname;
+            uris.context = context;
+            uris.runner = runner;
+            uris.applicationClassname = applicationClassname;
+        }
+        return uris;
+    }
+
+    // @Override
+    @Override
+    protected Class getApplicationClass() throws ClassNotFoundException {
+        // TODO use getClassLoader() ?
+        return getClass().getClassLoader().loadClass(applicationClassName);
+    }
+
+    @Override
+    String getRequestPathInfo(HttpServletRequest request) {
+        String path = request.getPathInfo();
+        if (path == null) {
+            return null;
+        }
+
+        path = path.substring(1 + applicationClassName.length());
+        return path;
+    }
+
+    @Override
+    String getWidgetsetLocation(HttpServletRequest request) {
+        URIS uris = getApplicationRunnerURIs(request);
+        String widgetsetPath = uris.widgetsetPath;
+        if (widgetsetPath.equals("/")) {
+            widgetsetPath = "";
+        }
+
+        return widgetsetPath;
+    }
+
+}
index 17b6c66c45c3ee478504c11a0a9add33b25e277a..bf26df9c5cc2c3532ac37c5941d76a2b007858cd 100644 (file)
@@ -4,47 +4,10 @@
 
 package com.itmill.toolkit.terminal.gwt.server;
 
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.xml.sax.SAXException;
 
 import com.itmill.toolkit.Application;
-import com.itmill.toolkit.Application.SystemMessages;
-import com.itmill.toolkit.external.org.apache.commons.fileupload.servlet.ServletFileUpload;
-import com.itmill.toolkit.service.FileTypeResolver;
-import com.itmill.toolkit.terminal.DownloadStream;
-import com.itmill.toolkit.terminal.ParameterHandler;
-import com.itmill.toolkit.terminal.Terminal;
-import com.itmill.toolkit.terminal.ThemeResource;
-import com.itmill.toolkit.terminal.URIHandler;
-import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
-import com.itmill.toolkit.ui.Window;
 
 /**
  * This servlet connects IT Mill Toolkit Application to Web.
@@ -56,106 +19,10 @@ import com.itmill.toolkit.ui.Window;
  */
 
 @SuppressWarnings("serial")
-public class ApplicationServlet extends HttpServlet {
-
-    /**
-     * Version number of this release. For example "5.0.0".
-     */
-    public static final String VERSION;
-
-    /**
-     * Major version number. For example 5 in 5.1.0.
-     */
-    public static final int VERSION_MAJOR;
-
-    /**
-     * Minor version number. For example 1 in 5.1.0.
-     */
-    public static final int VERSION_MINOR;
-
-    /**
-     * Builds number. For example 0-custom_tag in 5.0.0-custom_tag.
-     */
-    public static final String VERSION_BUILD;
-
-    /* Initialize version numbers from string replaced by build-script. */
-    static {
-        if ("@VERSION@".equals("@" + "VERSION" + "@")) {
-            VERSION = "5.9.9-INTERNAL-NONVERSIONED-DEBUG-BUILD";
-        } else {
-            VERSION = "@VERSION@";
-        }
-        final String[] digits = VERSION.split("\\.");
-        VERSION_MAJOR = Integer.parseInt(digits[0]);
-        VERSION_MINOR = Integer.parseInt(digits[1]);
-        VERSION_BUILD = digits[2];
-    }
-
-    /**
-     * If the attribute is present in the request, a html fragment will be
-     * written instead of a whole page.
-     */
-    public static final String REQUEST_FRAGMENT = ApplicationServlet.class
-            .getName()
-            + ".fragment";
-    /**
-     * This request attribute forces widgetset used; e.g for portlets that can
-     * not have different widgetsets.
-     */
-    public static final String REQUEST_WIDGETSET = ApplicationServlet.class
-            .getName()
-            + ".widgetset";
-    /**
-     * This request attribute is used to add styles to the main element. E.g
-     * "height:500px" generates a style="height:500px" to the main element,
-     * useful from some embedding situations (e.g portlet include.)
-     */
-    public static final String REQUEST_APPSTYLE = ApplicationServlet.class
-            .getName()
-            + ".style";
-
-    // Configurable parameter names
-    private static final String PARAMETER_DEBUG = "Debug";
-
-    private static final String PARAMETER_PRODUCTION_MODE = "productionMode";
-    private static final String NOT_PRODUCTION_MODE_INFO = "=================================================================\nIT Mill Toolkit is running in DEBUG MODE.\nAdd productionMode=true to web.xml to disable debug features.\nTo show debug window, add ?debug to your application URL.\n=================================================================";
-
-    private static final String PARAMETER_ITMILL_RESOURCES = "Resources";
-
-    private static final int DEFAULT_BUFFER_SIZE = 32 * 1024;
-
-    private static final int MAX_BUFFER_SIZE = 64 * 1024;
-
-    private static final String RESOURCE_URI = "/RES/";
-
-    private static final String AJAX_UIDL_URI = "/UIDL";
-
-    static final String THEME_DIRECTORY_PATH = "ITMILL/themes/";
-
-    private static final int DEFAULT_THEME_CACHETIME = 1000 * 60 * 60 * 24;
-
-    static final String WIDGETSET_DIRECTORY_PATH = "ITMILL/widgetsets/";
-
-    // Name of the default widget set, used if not specified in web.xml
-    private static final String DEFAULT_WIDGETSET = "com.itmill.toolkit.terminal.gwt.DefaultWidgetSet";
-
-    // Widget set parameter name
-    private static final String PARAMETER_WIDGETSET = "widgetset";
+public class ApplicationServlet extends AbstractApplicationServlet {
 
     // Private fields
-    private Class applicationClass;
-
-    private Properties applicationProperties;
-
-    private String resourcePath = null;
-
-    private boolean productionMode = false;
-
-    // Is this servlet application runner
-    boolean isApplicationRunnerServlet = false;
-
-    // If servlet is application runner, store request's classname
-    String applicationRunnerClassname = null;
+    private Class<? extends Application> applicationClass;
 
     /**
      * Called by the servlet container to indicate to a servlet that the servlet
@@ -168,1547 +35,50 @@ public class ApplicationServlet extends HttpServlet {
      *             if an exception has occurred that interferes with the
      *             servlet's normal operation.
      */
+    @SuppressWarnings("unchecked")
     @Override
     public void init(javax.servlet.ServletConfig servletConfig)
             throws javax.servlet.ServletException {
         super.init(servletConfig);
 
-        // Get applicationRunner
-        final String applicationRunner = servletConfig
-                .getInitParameter("applicationRunner");
-        if (applicationRunner != null) {
-            if ("true".equals(applicationRunner)) {
-                isApplicationRunnerServlet = true;
-            } else if ("false".equals(applicationRunner)) {
-                isApplicationRunnerServlet = false;
-            } else {
-                throw new ServletException(
-                        "If applicationRunner parameter is given for an application, it must be 'true' or 'false'");
-            }
-        }
-
-        // Stores the application parameters into Properties object
-        applicationProperties = new Properties();
-        for (final Enumeration e = servletConfig.getInitParameterNames(); e
-                .hasMoreElements();) {
-            final String name = (String) e.nextElement();
-            applicationProperties.setProperty(name, servletConfig
-                    .getInitParameter(name));
-        }
-
-        // Overrides with server.xml parameters
-        final ServletContext context = servletConfig.getServletContext();
-        for (final Enumeration e = context.getInitParameterNames(); e
-                .hasMoreElements();) {
-            final String name = (String) e.nextElement();
-            applicationProperties.setProperty(name, context
-                    .getInitParameter(name));
-        }
-
-        // Check if the application is in production mode.
-        // We are in production mode if Debug=false or productionMode=true
-        if (getApplicationOrSystemProperty(PARAMETER_DEBUG, "true").equals(
-                "false")) {
-            // "Debug=true" is the old way and should no longer be used
-            productionMode = true;
-        } else if (getApplicationOrSystemProperty(PARAMETER_PRODUCTION_MODE,
-                "false").equals("true")) {
-            // "productionMode=true" is the real way to do it
-            productionMode = true;
-        }
-
-        if (!productionMode) {
-            /* Print an information/warning message about running in debug mode */
-            System.err.println(NOT_PRODUCTION_MODE_INFO);
-        }
-
         // Loads the application class using the same class loader
         // as the servlet itself
-        if (!isApplicationRunnerServlet) {
-            // Gets the application class name
-            final String applicationClassName = servletConfig
-                    .getInitParameter("application");
-            if (applicationClassName == null) {
-                throw new ServletException(
-                        "Application not specified in servlet parameters");
-            }
-            try {
-                applicationClass = getClassLoader().loadClass(
-                        applicationClassName);
-            } catch (final ClassNotFoundException e) {
-                throw new ServletException("Failed to load application class: "
-                        + applicationClassName);
-            }
-        } else {
-            // This servlet is in application runner mode, it uses classloader
-            // later to create Applications based on URL
-        }
-
-    }
-
-    private ClassLoader getClassLoader() throws ServletException {
-        // Gets custom class loader
-        final String classLoaderName = getApplicationOrSystemProperty(
-                "ClassLoader", null);
-        ClassLoader classLoader;
-        if (classLoaderName == null) {
-            classLoader = getClass().getClassLoader();
-        } else {
-            try {
-                final Class<?> classLoaderClass = getClass().getClassLoader()
-                        .loadClass(classLoaderName);
-                final Constructor<?> c = classLoaderClass
-                        .getConstructor(new Class[] { ClassLoader.class });
-                classLoader = (ClassLoader) c
-                        .newInstance(new Object[] { getClass().getClassLoader() });
-            } catch (final Exception e) {
-                throw new ServletException(
-                        "Could not find specified class loader: "
-                                + classLoaderName, e);
-            }
-        }
-        return classLoader;
-    }
-
-    /**
-     * Gets an application or system property value.
-     * 
-     * @param parameterName
-     *            the Name or the parameter.
-     * @param defaultValue
-     *            the Default to be used.
-     * @return String value or default if not found
-     */
-    private String getApplicationOrSystemProperty(String parameterName,
-            String defaultValue) {
-
-        // Try application properties
-        String val = applicationProperties.getProperty(parameterName);
-        if (val != null) {
-            return val;
-        }
-
-        // Try lowercased application properties for backward compability with
-        // 3.0.2 and earlier
-        val = applicationProperties.getProperty(parameterName.toLowerCase());
-        if (val != null) {
-            return val;
-        }
-
-        // Try system properties
-        String pkgName;
-        final Package pkg = getClass().getPackage();
-        if (pkg != null) {
-            pkgName = pkg.getName();
-        } else {
-            final String className = getClass().getName();
-            pkgName = new String(className.toCharArray(), 0, className
-                    .lastIndexOf('.'));
-        }
-        val = System.getProperty(pkgName + "." + parameterName);
-        if (val != null) {
-            return val;
-        }
-
-        // Try lowercased system properties
-        val = System.getProperty(pkgName + "." + parameterName.toLowerCase());
-        if (val != null) {
-            return val;
-        }
-
-        return defaultValue;
-    }
-
-    /**
-     * Receives standard HTTP requests from the public service method and
-     * dispatches them.
-     * 
-     * @param request
-     *            the object that contains the request the client made of the
-     *            servlet.
-     * @param response
-     *            the object that contains the response the servlet returns to
-     *            the client.
-     * @throws ServletException
-     *             if an input or output error occurs while the servlet is
-     *             handling the TRACE request.
-     * @throws IOException
-     *             if the request for the TRACE cannot be handled.
-     */
-    @Override
-    protected void service(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException {
-
-        // check if we should serve static files (widgetsets, themes)
-        if ((request.getPathInfo() != null)
-                && (request.getPathInfo().length() > 10)) {
-            if ((request.getContextPath() != null)
-                    && (request.getRequestURI().startsWith("/ITMILL/"))) {
-                serveStaticResourcesInITMILL(request.getRequestURI(), response);
-                return;
-            } else if (request.getRequestURI().startsWith(
-                    request.getContextPath() + "/ITMILL/")) {
-                serveStaticResourcesInITMILL(request.getRequestURI().substring(
-                        request.getContextPath().length()), response);
-                return;
-            }
-        }
-
-        Application application = null;
-        boolean UIDLrequest = false;
-        try {
-            // handle file upload if multipart request
-            if (ServletFileUpload.isMultipartContent(request)) {
-                application = getExistingApplication(request, response);
-                if (application == null) {
-                    throw new SessionExpired();
-                }
-                // Invokes context transaction listeners
-                // note: endTransaction is called on finalize below
-                ((WebApplicationContext) application.getContext())
-                        .startTransaction(application, request);
-                ((WebApplicationContext) application.getContext())
-                        .getApplicationManager(application, this)
-                        .handleFileUpload(request, response);
-                return;
-            }
-
-            // Update browser details
-            final WebBrowser browser = WebApplicationContext
-                    .getApplicationContext(request.getSession()).getBrowser();
-            browser.updateBrowserProperties(request);
-            // TODO Add screen height and width to the GWT client
-
-            // Handles AJAX UIDL requests
-            if (request.getPathInfo() != null) {
-
-                String compare = AJAX_UIDL_URI;
-                if (isApplicationRunnerServlet) {
-                    final String[] URIparts = getApplicationRunnerURIs(request);
-                    applicationRunnerClassname = URIparts[4];
-                    compare = "/" + applicationRunnerClassname + AJAX_UIDL_URI;
-                }
-
-                if (request.getPathInfo().startsWith(compare + "/")
-                        || request.getPathInfo().endsWith(compare)) {
-                    UIDLrequest = true;
-                    application = getExistingApplication(request, response);
-                    if (application == null) {
-                        // No existing applications found
-                        final String repaintAll = request
-                                .getParameter("repaintAll");
-                        if ((repaintAll != null) && (repaintAll.equals("1"))) {
-                            // UIDL request contains valid repaintAll=1 event,
-                            // probably user wants to initiate new application
-                            // through custom index.html without writeAjaxPage
-                            application = getNewApplication(request, response);
-                        } else {
-                            // UIDL request refers to non-existing application
-                            throw new SessionExpired();
-                        }
-                    }
-
-                    // Invokes context transaction listeners
-                    // note: endTransaction is called on finalize below
-                    ((WebApplicationContext) application.getContext())
-                            .startTransaction(application, request);
 
-                    // Handle UIDL request
-                    ((WebApplicationContext) application.getContext())
-                            .getApplicationManager(application, this)
-                            .handleUidlRequest(request, response, this);
-                    return;
-                }
-            }
-
-            // Get existing application
-            application = getExistingApplication(request, response);
-            if (application == null
-                    || request.getParameter("restartApplication") != null
-                    || request.getParameter("closeApplication") != null) {
-                if (application != null) {
-                    application.close();
-                    final HttpSession session = request.getSession(false);
-                    if (session != null) {
-                        WebApplicationContext.getApplicationContext(session).applicationToAjaxAppMgrMap
-                                .remove(application);
-                        WebApplicationContext.getApplicationContext(session)
-                                .removeApplication(application);
-                    }
-                }
-                if (request.getParameter("closeApplication") != null) {
-                    return;
-                }
-                // Not found, creating new application
-                application = getNewApplication(request, response);
-            }
-
-            // Invokes context transaction listeners
-            // note: endTransaction is called on finalize below
-            ((WebApplicationContext) application.getContext())
-                    .startTransaction(application, request);
-
-            // Removes application if it has stopped
-            if (!application.isRunning()) {
-                endApplication(request, response, application);
-                return;
-            }
-
-            // Finds the window within the application
-            Window window = null;
-            window = getApplicationWindow(request, application);
-            if (window == null) {
-                throw new ServletException(
-                        "Application did not give any window, did you remember to setMainWindow()?");
-            }
-
-            // Handle parameters
-            final Map parameters = request.getParameterMap();
-            if (window != null && parameters != null) {
-                window.handleParameters(parameters);
-            }
-
-            // Is this a download request from application
-            DownloadStream download = null;
-
-            // Handles the URI if the application is still running
-            download = ((WebApplicationContext) application.getContext())
-                    .getApplicationManager(application, this).handleURI(window,
-                            request, response);
-
-            // If this is not a download request
-            if (download == null) {
-
-                // Sets terminal type for the window, if not already set
-                if (window.getTerminal() == null) {
-                    window.setTerminal(browser);
-                }
-
-                // Finds theme name
-                String themeName = window.getTheme();
-                if (request.getParameter("theme") != null) {
-                    themeName = request.getParameter("theme");
-                }
-
-                if (themeName == null) {
-                    themeName = "default";
-                }
-
-                // Handles resource requests
-                if (handleResourceRequest(request, response, themeName)) {
-                    return;
-                }
-
-                // Send initial AJAX page that kickstarts Toolkit application
-                writeAjaxPage(request, response, window, themeName, application);
-
-            } else {
-                // Client downloads an resource
-                handleDownload(download, request, response);
-            }
-
-        } catch (final SessionExpired e) {
-            // Session has expired, notify user
-            try {
-                if (!isOnUnloadRequest(request)) {
-                    Application.SystemMessages ci = getSystemMessages();
-                    if (!UIDLrequest) {
-                        // 'plain' http req - e.g. browser reload;
-                        // just go ahead redirect the browser
-                        response.sendRedirect(ci.getSessionExpiredURL());
-                    } else {
-                        // send uidl redirect
-                        criticalNotification(request, response, ci
-                                .getSessionExpiredCaption(), ci
-                                .getSessionExpiredMessage(), ci
-                                .getSessionExpiredURL());
-                        // Invalidate session (weird to have session if we're
-                        // saying
-                        // that it's expired, and worse: portal integration will
-                        // fail since the session is not created by the portal.
-                        request.getSession().invalidate();
-                    }
-                }
-            } catch (SystemMessageException ee) {
-                throw new ServletException(ee);
-            }
-
-        } catch (final GeneralSecurityException e) {
-            if (!isOnUnloadRequest(request)) {
-                // TODO handle differently?
-                // Invalid security key, show session expired message for now.
-                try {
-                    Application.SystemMessages ci = getSystemMessages();
-                    if (!UIDLrequest) {
-                        // 'plain' http req - e.g. browser reload;
-                        // just go ahead redirect the browser
-                        response.sendRedirect(ci.getSessionExpiredURL());
-                    } else {
-                        // send uidl redirect
-                        criticalNotification(request, response, ci
-                                .getSessionExpiredCaption(), ci
-                                .getSessionExpiredMessage(), ci
-                                .getSessionExpiredURL());
-                    }
-                    request.getSession().invalidate();
-                } catch (SystemMessageException ee) {
-                    throw new ServletException(ee);
-                }
-            }
-        } catch (final Throwable e) {
-            // if this was an UIDL request, response UIDL back to client
-            if (UIDLrequest) {
-                Application.SystemMessages ci = getSystemMessages();
-                criticalNotification(request, response, ci
-                        .getInternalErrorCaption(), ci
-                        .getInternalErrorMessage(), ci.getInternalErrorURL());
-                if (application != null) {
-                    application.getErrorHandler().terminalError(
-                            new RequestError(e));
-                } else {
-                    throw new ServletException(e);
-                }
-            } else {
-                // Re-throw other exceptions
-                throw new ServletException(e);
-            }
-        } finally {
-            // Notifies transaction end
-            if (application != null) {
-                ((WebApplicationContext) application.getContext())
-                        .endTransaction(application, request);
-            }
-
-            // Work-around for GAE session problem. Explicitly touch session so
-            // it is re-serialized.
-            request.getSession().setAttribute("sessionUpdated",
-                    new Date().getTime());
+        // Gets the application class name
+        final String applicationClassName = servletConfig
+                .getInitParameter("application");
+        if (applicationClassName == null) {
+            throw new ServletException(
+                    "Application not specified in servlet parameters");
         }
-    }
-
-    private boolean isOnUnloadRequest(HttpServletRequest request) {
-        return request.getParameter(ApplicationConnection.PARAM_UNLOADBURST) != null;
-    }
 
-    /** Get system messages from the current application class */
-    private SystemMessages getSystemMessages() {
         try {
-            Class appCls = applicationClass;
-            if (isApplicationRunnerServlet) {
-                appCls = getClass().getClassLoader().loadClass(
-                        applicationRunnerClassname);
-            }
-            Method m = appCls.getMethod("getSystemMessages", (Class[]) null);
-            return (Application.SystemMessages) m.invoke(null, (Object[]) null);
-        } catch (ClassNotFoundException e) {
-            // This should never happen
-            throw new SystemMessageException(e);
-        } catch (SecurityException e) {
-            throw new SystemMessageException(
-                    "Application.getSystemMessage() should be static public", e);
-        } catch (NoSuchMethodException e) {
-            // This is completely ok and should be silently ignored
-        } catch (IllegalArgumentException e) {
-            // This should never happen
-            throw new SystemMessageException(e);
-        } catch (IllegalAccessException e) {
-            throw new SystemMessageException(
-                    "Application.getSystemMessage() should be static public", e);
-        } catch (InvocationTargetException e) {
-            // This should never happen
-            throw new SystemMessageException(e);
-        }
-        return Application.getSystemMessages();
-    }
-
-    /**
-     * Serve resources in ITMILL directory if requested.
-     * 
-     * @param request
-     * @param response
-     * @throws IOException
-     * @throws ServletException
-     */
-    private void serveStaticResourcesInITMILL(String filename,
-            HttpServletResponse response) throws IOException, ServletException {
-
-        final ServletContext sc = getServletContext();
-        InputStream is = sc.getResourceAsStream(filename);
-        if (is == null) {
-            // try if requested file is found from classloader
-
-            // strip leading "/" otherwise stream from JAR wont work
-            filename = filename.substring(1);
-            is = getClassLoader().getResourceAsStream(filename);
-
-            if (is == null) {
-                // cannot serve requested file
-                System.err
-                        .println("Requested resource ["
-                                + filename
-                                + "] not found from filesystem or through class loader."
-                                + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/ITMILL folder.");
-                response.setStatus(404);
-                return;
-            }
-        }
-        final String mimetype = sc.getMimeType(filename);
-        if (mimetype != null) {
-            response.setContentType(mimetype);
-        }
-        final OutputStream os = response.getOutputStream();
-        final byte buffer[] = new byte[20000];
-        int bytes;
-        while ((bytes = is.read(buffer)) >= 0) {
-            os.write(buffer, 0, bytes);
-        }
-    }
-
-    /**
-     * Send notification to client's application. Used to notify client of
-     * critical errors and session expiration due to long inactivity. Server has
-     * no knowledge of what application client refers to.
-     * 
-     * @param request
-     *            the HTTP request instance.
-     * @param response
-     *            the HTTP response to write to.
-     * @param caption
-     *            for the notification
-     * @param message
-     *            for the notification
-     * @param url
-     *            url to load after message, null for current page
-     * @throws IOException
-     *             if the writing failed due to input/output error.
-     */
-    void criticalNotification(HttpServletRequest request,
-            HttpServletResponse response, String caption, String message,
-            String url) throws IOException {
-
-        // clients JS app is still running, but server application either
-        // no longer exists or it might fail to perform reasonably.
-        // send a notification to client's application and link how
-        // to "restart" application.
-
-        if (caption != null) {
-            caption = "\"" + caption + "\"";
-        }
-        if (message != null) {
-            message = "\"" + message + "\"";
-        }
-        if (url != null) {
-            url = "\"" + url + "\"";
-        }
-
-        // Set the response type
-        response.setContentType("application/json; charset=UTF-8");
-        final ServletOutputStream out = response.getOutputStream();
-        final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
-                new OutputStreamWriter(out, "UTF-8")));
-        outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {"
-                + "\"appError\": {" + "\"caption\":" + caption + ","
-                + "\"message\" : " + message + "," + "\"url\" : " + url
-                + "}}, \"resources\": {}, \"locales\":[]}]");
-        outWriter.flush();
-        outWriter.close();
-        out.flush();
-    }
-
-    /**
-     * Resolve application URL and widgetset URL. Widgetset is not application
-     * specific.
-     * 
-     * @param request
-     * @return string array consisting of application url first and then
-     *         widgetset url.
-     * @throws MalformedURLException
-     */
-    private String[] getAppAndWidgetUrl(HttpServletRequest request)
-            throws MalformedURLException {
-        // don't use server and port in uri. It may cause problems with some
-        // virtual server configurations which lose the server name
-        String appUrl = null;
-        String widgetsetUrl = null;
-        if (isApplicationRunnerServlet) {
-            final String[] URIparts = getApplicationRunnerURIs(request);
-            widgetsetUrl = URIparts[0];
-            if (widgetsetUrl.equals("/")) {
-                widgetsetUrl = "";
-            }
-            appUrl = URIparts[1];
-        } else {
-            String[] urlParts;
-            urlParts = getApplicationUrl(request).toString().split("\\/");
-            appUrl = "";
-            widgetsetUrl = "";
-            // if context is specified add it to widgetsetUrl
-            String ctxPath = request.getContextPath();
-            if (ctxPath.length() == 0
-                    && request
-                            .getAttribute("javax.servlet.include.context_path") != null) {
-                // include request (e.g portlet), get contex path from
-                // attribute
-                ctxPath = (String) request
-                        .getAttribute("javax.servlet.include.context_path");
-            }
-            if (urlParts.length > 3
-                    && urlParts[3].equals(ctxPath.replaceAll("\\/", ""))) {
-                widgetsetUrl += "/" + urlParts[3];
-            }
-            for (int i = 3; i < urlParts.length; i++) {
-                appUrl += "/" + urlParts[i];
-            }
-            if (appUrl.endsWith("/")) {
-                appUrl = appUrl.substring(0, appUrl.length() - 1);
-            }
-
+            applicationClass = (Class<? extends Application>) getClassLoader()
+                    .loadClass(applicationClassName);
+        } catch (final ClassNotFoundException e) {
+            throw new ServletException("Failed to load application class: "
+                    + applicationClassName);
         }
-        return new String[] { appUrl, widgetsetUrl };
     }
 
-    /**
-     * 
-     * @param request
-     *            the HTTP request.
-     * @param response
-     *            the HTTP response to write to.
-     * @param out
-     * @param unhandledParameters
-     * @param window
-     * @param terminalType
-     * @param theme
-     * @throws IOException
-     *             if the writing failed due to input/output error.
-     * @throws MalformedURLException
-     *             if the application is denied access the persistent data store
-     *             represented by the given URL.
-     */
-    private void writeAjaxPage(HttpServletRequest request,
-            HttpServletResponse response, Window window, String themeName,
-            Application application) throws IOException, MalformedURLException,
-            ServletException {
-
-        // e.g portlets only want a html fragment
-        boolean fragment = (request.getAttribute(REQUEST_FRAGMENT) != null);
-        if (fragment) {
-            request.setAttribute(Application.class.getName(), application);
-        }
-
-        final BufferedWriter page = new BufferedWriter(new OutputStreamWriter(
-                response.getOutputStream()));
-        String pathInfo = request.getPathInfo() == null ? "/" : request
-                .getPathInfo();
-        if (isApplicationRunnerServlet) {
-            pathInfo = pathInfo
-                    .substring(applicationRunnerClassname.length() + 1);
-        }
-        String title = ((window == null || window.getCaption() == null) ? "IT Mill Toolkit 5"
-                : window.getCaption());
-
-        String widgetset = null;
-        // request widgetset takes precedence (e.g portlet include)
-        Object reqParam = request.getAttribute(REQUEST_WIDGETSET);
-        try {
-            widgetset = (String) reqParam;
-        } catch (Exception e) {
-            // FIXME: Handle exception
-            System.err.println("Warning: request param '" + REQUEST_WIDGETSET
-                    + "' could not be used (is not a String)" + e);
-        }
-        if (widgetset == null) {
-            widgetset = applicationProperties.getProperty(PARAMETER_WIDGETSET);
-        }
-        if (widgetset == null) {
-            widgetset = DEFAULT_WIDGETSET;
-        }
-        final String[] urls = getAppAndWidgetUrl(request);
-        final String appUrl = urls[0];
-        final String widgetsetUrl = urls[1];
-
-        final String staticFilePath = getApplicationOrSystemProperty(
-                PARAMETER_ITMILL_RESOURCES, widgetsetUrl);
-
-        // Default theme does not use theme URI
-        String themeUri = null;
-        if (themeName != null) {
-            // Using custom theme
-            themeUri = staticFilePath + "/" + THEME_DIRECTORY_PATH + themeName;
-        }
-
-        if (!fragment) {
-            // Window renders are not cacheable
-            response.setHeader("Cache-Control", "no-cache");
-            response.setHeader("Pragma", "no-cache");
-            response.setDateHeader("Expires", 0);
-            response.setContentType("text/html; charset=UTF-8");
-
-            // write html header
-            page.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD "
-                    + "XHTML 1.0 Transitional//EN\" "
-                    + "\"http://www.w3.org/TR/xhtml1/"
-                    + "DTD/xhtml1-transitional.dtd\">\n");
-
-            page.write("<html xmlns=\"http://www.w3.org/1999/xhtml\""
-                    + ">\n<head>\n");
-            page
-                    .write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n");
-            page
-                    .write("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=7\" />\n");
-            page.write("<style type=\"text/css\">"
-                    + "html, body {height:100%;}</style>");
-
-            // Add favicon links
-            page
-                    .write("<link rel=\"shortcut icon\" type=\"image/vnd.microsoft.icon\" href=\""
-                            + themeUri + "/favicon.ico\" />");
-            page
-                    .write("<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\""
-                            + themeUri + "/favicon.ico\" />");
-
-            page.write("<title>" + title + "</title>");
-
-            page
-                    .write("\n</head>\n<body scroll=\"auto\" class=\"i-generated-body\">\n");
-        }
-
-        String appId = appUrl;
-        if ("".equals(appUrl)) {
-            appId = "ROOT";
-        }
-        appId = appId.replaceAll("[^a-zA-Z0-9]", "");
-        // Add hashCode to the end, so that it is still (sort of) predictable,
-        // but indicates that it should not be used in CSS and such:
-        int hashCode = appId.hashCode();
-        if (hashCode < 0) {
-            hashCode = -hashCode;
-        }
-        appId = appId + "-" + hashCode;
-
-        // Get system messages
-        Application.SystemMessages systemMessages = null;
-        try {
-            systemMessages = getSystemMessages();
-        } catch (SystemMessageException e) {
-            // failing to get the system messages is always a problem
-            throw new ServletException("CommunicationError!", e);
-        }
-
-        if (isGecko17(request)) {
-            // special start page for gecko 1.7 versions. Firefox 1.0 is not
-            // supported, but the hack is make it possible to use linux and
-            // hosted mode browser for debugging. Note that due this hack,
-            // debugging gwt code in portals with linux will be problematic if
-            // there are multiple toolkit portlets visible at the same time.
-            // TODO remove this when hosted mode on linux gets newer gecko
-
-            page.write("<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
-                    + "style=\"width:0;height:0;border:0;overflow:"
-                    + "hidden\" src=\"javascript:false\"></iframe>\n");
-            page.write("<script language='javascript' src='" + staticFilePath
-                    + "/" + WIDGETSET_DIRECTORY_PATH + widgetset + "/"
-                    + widgetset + ".nocache.js?" + new Date().getTime()
-                    + "'></script>\n");
-            page.write("<script type=\"text/javascript\">\n");
-            page.write("//<![CDATA[\n");
-            page.write("if(!itmill || !itmill.toolkitConfigurations) {\n "
-                    + "if(!itmill) { var itmill = {}} \n"
-                    + "itmill.toolkitConfigurations = {};\n"
-                    + "itmill.themesLoaded = {}};\n");
-
-            if (!isProductionMode()) {
-                page.write("itmill.debug = true;\n");
-            }
-
-            page.write("itmill.toolkitConfigurations[\"" + appId + "\"] = {");
-            page.write("appUri:'" + appUrl + "', ");
-            page.write("pathInfo: '" + pathInfo + "', ");
-            if (window != application.getMainWindow()) {
-                page.write("windowName: '" + window.getName() + "', ");
-            }
-            page.write("themeUri:");
-            page.write(themeUri != null ? "'" + themeUri + "'" : "null");
-            page.write(", versionInfo : {toolkitVersion:\"");
-            page.write(VERSION);
-            page.write("\",applicationVersion:\"");
-            page.write(application.getVersion());
-            page.write("\"},");
-            if (systemMessages != null) {
-                // Write the CommunicationError -message to client
-                String caption = systemMessages.getCommunicationErrorCaption();
-                if (caption != null) {
-                    caption = "\"" + caption + "\"";
-                }
-                String message = systemMessages.getCommunicationErrorMessage();
-                if (message != null) {
-                    message = "\"" + message + "\"";
-                }
-                String url = systemMessages.getCommunicationErrorURL();
-                if (url != null) {
-                    url = "\"" + url + "\"";
-                }
-                page.write("\"comErrMsg\": {" + "\"caption\":" + caption + ","
-                        + "\"message\" : " + message + "," + "\"url\" : " + url
-                        + "}");
-            }
-            page.write("};\n//]]>\n</script>\n");
-
-            if (themeName != null) {
-                // Custom theme's stylesheet, load only once, in different
-                // script
-                // tag to be dominate styles injected by widget
-                // set
-                page.write("<script type=\"text/javascript\">\n");
-                page.write("//<![CDATA[\n");
-                page.write("if(!itmill.themesLoaded['" + themeName + "']) {\n");
-                page
-                        .write("var stylesheet = document.createElement('link');\n");
-                page.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
-                page.write("stylesheet.setAttribute('type', 'text/css');\n");
-                page.write("stylesheet.setAttribute('href', '" + themeUri
-                        + "/styles.css');\n");
-                page
-                        .write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
-                page.write("itmill.themesLoaded['" + themeName
-                        + "'] = true;\n}\n");
-                page.write("//]]>\n</script>\n");
-            }
-
-        } else {
-            page.write("<script type=\"text/javascript\">\n");
-            page.write("//<![CDATA[\n");
-            page.write("if(!itmill || !itmill.toolkitConfigurations) {\n "
-                    + "if(!itmill) { var itmill = {}} \n"
-                    + "itmill.toolkitConfigurations = {};\n"
-                    + "itmill.themesLoaded = {};\n");
-            if (!isProductionMode()) {
-                page.write("itmill.debug = true;\n");
-            }
-            page
-                    .write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
-                            + "style=\"width:0;height:0;border:0;overflow:"
-                            + "hidden\" src=\"javascript:false\"></iframe>');\n");
-            page.write("document.write(\"<script language='javascript' src='"
-                    + staticFilePath + "/" + WIDGETSET_DIRECTORY_PATH
-                    + widgetset + "/" + widgetset + ".nocache.js?"
-                    + new Date().getTime() + "'><\\/script>\");\n}\n");
-
-            page.write("itmill.toolkitConfigurations[\"" + appId + "\"] = {");
-            page.write("appUri:'" + appUrl + "', ");
-            page.write("pathInfo: '" + pathInfo + "', ");
-            if (window != application.getMainWindow()) {
-                page.write("windowName: '" + window.getName() + "', ");
-            }
-            page.write("themeUri:");
-            page.write(themeUri != null ? "'" + themeUri + "'" : "null");
-            page.write(", versionInfo : {toolkitVersion:\"");
-            page.write(VERSION);
-            page.write("\",applicationVersion:\"");
-            page.write(application.getVersion());
-            page.write("\"},");
-            if (systemMessages != null) {
-                // Write the CommunicationError -message to client
-                String caption = systemMessages.getCommunicationErrorCaption();
-                if (caption != null) {
-                    caption = "\"" + caption + "\"";
-                }
-                String message = systemMessages.getCommunicationErrorMessage();
-                if (message != null) {
-                    message = "\"" + message + "\"";
-                }
-                String url = systemMessages.getCommunicationErrorURL();
-                if (url != null) {
-                    url = "\"" + url + "\"";
-                }
-
-                page.write("\"comErrMsg\": {" + "\"caption\":" + caption + ","
-                        + "\"message\" : " + message + "," + "\"url\" : " + url
-                        + "}");
-            }
-            page.write("};\n//]]>\n</script>\n");
-
-            if (themeName != null) {
-                // Custom theme's stylesheet, load only once, in different
-                // script
-                // tag to be dominate styles injected by widget
-                // set
-                page.write("<script type=\"text/javascript\">\n");
-                page.write("//<![CDATA[\n");
-                page.write("if(!itmill.themesLoaded['" + themeName + "']) {\n");
-                page
-                        .write("var stylesheet = document.createElement('link');\n");
-                page.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
-                page.write("stylesheet.setAttribute('type', 'text/css');\n");
-                page.write("stylesheet.setAttribute('href', '" + themeUri
-                        + "/styles.css');\n");
-                page
-                        .write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
-                page.write("itmill.themesLoaded['" + themeName
-                        + "'] = true;\n}\n");
-                page.write("//]]>\n</script>\n");
-            }
-        }
-
-        // Warn if the widgetset has not been loaded after 15 seconds on
-        // inactivity
-        page.write("<script type=\"text/javascript\">\n");
-        page.write("//<![CDATA[\n");
-        page.write("setTimeout('if (typeof " + widgetset.replace('.', '_')
-                + " == \"undefined\") {alert(\"Failed to load the widgetset: "
-                + staticFilePath + "/" + WIDGETSET_DIRECTORY_PATH + widgetset
-                + "/" + widgetset + ".nocache.js\")};',15000);\n"
-                + "//]]>\n</script>\n");
-
-        String style = null;
-        reqParam = request.getAttribute(REQUEST_APPSTYLE);
-        if (reqParam != null) {
-            style = "style=\"" + reqParam + "\"";
-        }
-        /*- Add classnames; 
-         *      .i-app 
-         *      .i-app-loading
-         *      .i-app-<simpleName for app class> 
-         *      .i-theme-<themeName, remove non-alphanum>
-         */
-        // FIXME Can themeName be null? This possibility is taken into account
-        // in other places
-        page.write("<div id=\"" + appId
-                + "\" class=\"i-app i-app-loading i-theme-"
-                + themeName.replaceAll("[^a-zA-Z0-9]", "") + " i-app-"
-                + applicationClass.getSimpleName() + "\" "
-                + (style != null ? style : "") + "></div>\n");
-
-        if (!fragment) {
-            page.write("<noscript>" + getNoScriptMessage() + "</noscript>");
-            page.write("</body>\n</html>\n");
-        }
-        page.close();
-
-    }
-
-    /**
-     * Returns a message printed for browsers without scripting support or if
-     * browsers scripting support is disabled.
-     */
-    protected String getNoScriptMessage() {
-        return "You have to enable javascript in your browser to use an application built with IT Mill Toolkit.";
-    }
-
-    private boolean isGecko17(HttpServletRequest request) {
-        final WebBrowser browser = WebApplicationContext.getApplicationContext(
-                request.getSession()).getBrowser();
-        if (browser != null && browser.getBrowserApplication() != null) {
-            if (browser.getBrowserApplication().indexOf("rv:1.7.") > 0
-                    && browser.getBrowserApplication().indexOf("Gecko") > 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Handles the requested URI. An application can add handlers to do special
-     * processing, when a certain URI is requested. The handlers are invoked
-     * before any windows URIs are processed and if a DownloadStream is returned
-     * it is sent to the client.
-     * 
-     * @param stream
-     *            the download stream.
-     * 
-     * @param request
-     *            the HTTP request instance.
-     * @param response
-     *            the HTTP response to write to.
-     * @throws IOException
-     * 
-     * @see com.itmill.toolkit.terminal.URIHandler
-     */
-    private void handleDownload(DownloadStream stream,
-            HttpServletRequest request, HttpServletResponse response)
-            throws IOException {
-
-        if (stream.getParameter("Location") != null) {
-            response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
-            response.addHeader("Location", stream.getParameter("Location"));
-            return;
-        }
-
-        // Download from given stream
-        final InputStream data = stream.getStream();
-        if (data != null) {
-
-            // Sets content type
-            response.setContentType(stream.getContentType());
-
-            // Sets cache headers
-            final long cacheTime = stream.getCacheTime();
-            if (cacheTime <= 0) {
-                response.setHeader("Cache-Control", "no-cache");
-                response.setHeader("Pragma", "no-cache");
-                response.setDateHeader("Expires", 0);
-            } else {
-                response.setHeader("Cache-Control", "max-age=" + cacheTime
-                        / 1000);
-                response.setDateHeader("Expires", System.currentTimeMillis()
-                        + cacheTime);
-                response.setHeader("Pragma", "cache"); // Required to apply
-                // caching in some
-                // Tomcats
-            }
-
-            // Copy download stream parameters directly
-            // to HTTP headers.
-            final Iterator i = stream.getParameterNames();
-            if (i != null) {
-                while (i.hasNext()) {
-                    final String param = (String) i.next();
-                    response.setHeader(param, stream.getParameter(param));
-                }
-            }
-
-            // suggest local filename from DownloadStream if Content-Disposition
-            // not explicitly set
-            String contentDispositionValue = stream
-                    .getParameter("Content-Disposition");
-            if (contentDispositionValue == null) {
-                contentDispositionValue = "filename=\"" + stream.getFileName()
-                        + "\"";
-                response.setHeader("Content-Disposition",
-                        contentDispositionValue);
-            }
-
-            int bufferSize = stream.getBufferSize();
-            if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) {
-                bufferSize = DEFAULT_BUFFER_SIZE;
-            }
-            final byte[] buffer = new byte[bufferSize];
-            int bytesRead = 0;
-
-            final OutputStream out = response.getOutputStream();
-
-            while ((bytesRead = data.read(buffer)) > 0) {
-                out.write(buffer, 0, bytesRead);
-                out.flush();
-            }
-            out.close();
-
-        }
-
-    }
-
-    /**
-     * Handles theme resource file requests. Resources supplied with the themes
-     * are provided by the WebAdapterServlet.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @param response
-     *            the HTTP response.
-     * @return boolean <code>true</code> if the request was handled and further
-     *         processing should be suppressed, <code>false</code> otherwise.
-     * @throws ServletException
-     *             if an exception has occurred that interferes with the
-     *             servlet's normal operation.
-     */
-    private boolean handleResourceRequest(HttpServletRequest request,
-            HttpServletResponse response, String themeName)
+    @Override
+    protected Application getNewApplication(HttpServletRequest request)
             throws ServletException {
 
-        // If the resource path is unassigned, initialize it
-        if (resourcePath == null) {
-            resourcePath = request.getContextPath() + request.getServletPath()
-                    + RESOURCE_URI;
-            // WebSphere Application Server related fix
-            resourcePath = resourcePath.replaceAll("//", "/");
-        }
-
-        String resourceId = request.getPathInfo();
-
-        // Checks if this really is a resource request
-        if (resourceId == null || !resourceId.startsWith(RESOURCE_URI)) {
-            return false;
-        }
-
-        // Checks the resource type
-        resourceId = resourceId.substring(RESOURCE_URI.length());
-        InputStream data = null;
-
-        // Gets theme resources
+        // Creates a new application instance
         try {
-            data = getServletContext().getResourceAsStream(
-                    THEME_DIRECTORY_PATH + themeName + "/" + resourceId);
-        } catch (final Exception e) {
-            // FIXME: Handle exception
-            e.printStackTrace();
-            data = null;
-        }
-
-        // Writes the response
-        try {
-            if (data != null) {
-                response.setContentType(FileTypeResolver
-                        .getMIMEType(resourceId));
-
-                // Use default cache time for theme resources
-                response.setHeader("Cache-Control", "max-age="
-                        + DEFAULT_THEME_CACHETIME / 1000);
-                response.setDateHeader("Expires", System.currentTimeMillis()
-                        + DEFAULT_THEME_CACHETIME);
-                response.setHeader("Pragma", "cache"); // Required to apply
-                // caching in some
-                // Tomcats
-
-                // Writes the data to client
-                final byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
-                int bytesRead = 0;
-                final OutputStream out = response.getOutputStream();
-                while ((bytesRead = data.read(buffer)) > 0) {
-                    out.write(buffer, 0, bytesRead);
-                }
-                out.close();
-                data.close();
-            } else {
-                response.sendError(HttpServletResponse.SC_NOT_FOUND);
-            }
-
-        } catch (final java.io.IOException e) {
-            // FIXME: Handle exception
-            System.err.println("Resource transfer failed:  "
-                    + request.getRequestURI() + ". (" + e.getMessage() + ")");
-        }
-
-        return true;
-    }
-
-    /**
-     * Gets the current application URL from request.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @throws MalformedURLException
-     *             if the application is denied access to the persistent data
-     *             store represented by the given URL.
-     */
-    private URL getApplicationUrl(HttpServletRequest request)
-            throws MalformedURLException {
-
-        URL applicationUrl;
-
-        final URL reqURL = new URL(
-                (request.isSecure() ? "https://" : "http://")
-                        + request.getServerName()
-                        + ((request.isSecure() && request.getServerPort() == 443)
-                                || (!request.isSecure() && request
-                                        .getServerPort() == 80) ? "" : ":"
-                                + request.getServerPort())
-                        + request.getRequestURI());
-        String servletPath = "";
-        if (request.getAttribute("javax.servlet.include.servlet_path") != null) {
-            // this is an include request
-            servletPath = request.getAttribute(
-                    "javax.servlet.include.context_path").toString()
-                    + request
-                            .getAttribute("javax.servlet.include.servlet_path");
-
-        } else {
-            servletPath = request.getContextPath() + request.getServletPath();
-        }
-        if (servletPath.length() == 0
-                || servletPath.charAt(servletPath.length() - 1) != '/') {
-            servletPath = servletPath + "/";
-        }
-        applicationUrl = new URL(reqURL, servletPath);
-
-        return applicationUrl;
-    }
-
-    /**
-     * Parses application runner URIs.
-     * 
-     * If request URL is e.g.
-     * http://localhost:8080/itmill/run/com.itmill.toolkit.demo.Calc then
-     * <ul>
-     * <li>context=itmill</li>
-     * <li>Runner servlet=run</li>
-     * <li>Toolkit application=com.itmill.toolkit.demo.Calc</li>
-     * </ul>
-     * 
-     * @param request
-     * @return string array containing widgetset URI, application URI and
-     *         context, runner, application classname
-     */
-    private String[] getApplicationRunnerURIs(HttpServletRequest request) {
-        final String[] urlParts = request.getRequestURI().toString().split(
-                "\\/");
-        String context = null;
-        String runner = null;
-        String applicationClassname = null;
-        if (urlParts[1].equals(request.getContextPath().replaceAll("\\/", ""))) {
-            // class name comes after web context and runner application
-            context = urlParts[1];
-            runner = urlParts[2];
-            applicationClassname = urlParts[3];
-            return new String[] { "/" + context,
-                    "/" + context + "/" + runner + "/" + applicationClassname,
-                    context, runner, applicationClassname };
-        } else {
-            // no context
-            context = "";
-            runner = urlParts[1];
-            applicationClassname = urlParts[2];
-            return new String[] { "/",
-                    "/" + runner + "/" + applicationClassname, context, runner,
-                    applicationClassname };
-        }
-    }
-
-    /**
-     * Gets the existing application for given request. Looks for application
-     * instance for given request based on the requested URL.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @param response
-     * @return Application instance, or null if the URL does not map to valid
-     *         application.
-     * @throws MalformedURLException
-     *             if the application is denied access to the persistent data
-     *             store represented by the given URL.
-     * @throws SAXException
-     * @throws IllegalAccessException
-     * @throws InstantiationException
-     */
-    private Application getExistingApplication(HttpServletRequest request,
-            HttpServletResponse response) throws MalformedURLException,
-            SAXException, IllegalAccessException, InstantiationException {
-
-        // Ensures that the session is still valid
-        final HttpSession session = request.getSession(true);
-
-        // Gets application list for the session.
-        final Collection applications = WebApplicationContext
-                .getApplicationContext(session).getApplications();
-
-        // Search for the application (using the application URI) from the list
-        for (final Iterator i = applications.iterator(); i.hasNext();) {
-            final Application a = (Application) i.next();
-            final String aPath = a.getURL().getPath();
-            String servletPath = "";
-            if (isApplicationRunnerServlet) {
-                final String[] URIparts = getApplicationRunnerURIs(request);
-                servletPath = URIparts[1] + "/";
-            } else {
-                servletPath = request.getContextPath()
-                        + request.getServletPath();
-                if (servletPath.length() < aPath.length()) {
-                    servletPath += "/";
-                }
-            }
-            if (servletPath.equals(aPath)) {
-                // Found a running application
-                if (a.isRunning()) {
-                    return a;
-                }
-                // Application has stopped, so remove it before creating a new
-                // application
-                WebApplicationContext.getApplicationContext(session)
-                        .removeApplication(a);
-                break;
-            }
-        }
-
-        // Existing application not found
-        return null;
-    }
-
-    /**
-     * Creates new application for given request.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @param response
-     * @return Application instance, or null if the URL does not map to valid
-     *         application.
-     * @throws MalformedURLException
-     *             if the application is denied access to the persistent data
-     *             store represented by the given URL.
-     * @throws SAXException
-     * @throws IllegalAccessException
-     * @throws InstantiationException
-     * @throws ServletException
-     */
-    private Application getNewApplication(HttpServletRequest request,
-            HttpServletResponse response) throws MalformedURLException,
-            SAXException, IllegalAccessException, InstantiationException,
-            ServletException {
-
-        // Create application
-        final WebApplicationContext context = WebApplicationContext
-                .getApplicationContext(request.getSession());
-        final URL applicationUrl;
-
-        if (isApplicationRunnerServlet) {
-            final String[] URIparts = getApplicationRunnerURIs(request);
-            final String applicationClassname = URIparts[4];
-            applicationUrl = new URL(getApplicationUrl(request).toString()
-                    + applicationClassname + "/");
-            try {
-                applicationClass = getClassLoader().loadClass(
-                        applicationClassname);
-            } catch (final ClassNotFoundException e) {
-                throw new InstantiationException(
-                        "Failed to load application class: "
-                                + applicationClassname);
-            }
-        } else {
-            applicationUrl = getApplicationUrl(request);
-        }
-
-        // Creates new application and start it
-        try {
-            final Application application = (Application) applicationClass
-                    .newInstance();
-            context.addApplication(application);
-
-            // Sets initial locale from the request
-            application.setLocale(request.getLocale());
-
-            // Starts application
-            application.start(applicationUrl, applicationProperties, context);
+            final Application application = getApplicationClass().newInstance();
 
             return application;
-
         } catch (final IllegalAccessException e) {
-            throw e;
+            throw new ServletException("getNewApplication failed", e);
         } catch (final InstantiationException e) {
-            throw e;
-        }
-    }
-
-    /**
-     * Ends the application.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @param response
-     *            the HTTP response to write to.
-     * @param application
-     *            the application to end.
-     * @throws IOException
-     *             if the writing failed due to input/output error.
-     */
-    private void endApplication(HttpServletRequest request,
-            HttpServletResponse response, Application application)
-            throws IOException {
-
-        String logoutUrl = application.getLogoutURL();
-        if (logoutUrl == null) {
-            logoutUrl = application.getURL().toString();
-        }
-
-        final HttpSession session = request.getSession();
-        if (session != null) {
-            WebApplicationContext.getApplicationContext(session)
-                    .removeApplication(application);
-        }
-
-        response.sendRedirect(response.encodeRedirectURL(logoutUrl));
-    }
-
-    /**
-     * Gets the existing application or create a new one. Get a window within an
-     * application based on the requested URI.
-     * 
-     * @param request
-     *            the HTTP Request.
-     * @param application
-     *            the Application to query for window.
-     * @return Window matching the given URI or null if not found.
-     * @throws ServletException
-     *             if an exception has occurred that interferes with the
-     *             servlet's normal operation.
-     */
-    private Window getApplicationWindow(HttpServletRequest request,
-            Application application) throws ServletException {
-
-        Window window = null;
-
-        // Finds the window where the request is handled
-        String path = request.getPathInfo();
-        if (isApplicationRunnerServlet) {
-            path = path.substring(1 + applicationRunnerClassname.length());
-        }
-
-        // Main window as the URI is empty
-        if (path == null || path.length() == 0 || path.equals("/")
-                || path.startsWith("/APP/")) {
-            window = application.getMainWindow();
-        } else {
-            String windowName = null;
-            if (path.charAt(0) == '/') {
-                path = path.substring(1);
-            }
-            final int index = path.indexOf('/');
-            if (index < 0) {
-                windowName = path;
-                path = "";
-            } else {
-                windowName = path.substring(0, index);
-                path = path.substring(index + 1);
-            }
-            window = application.getWindow(windowName);
-
-            if (window == null) {
-                // By default, we use main window
-                window = application.getMainWindow();
-            } else if (!window.isVisible()) {
-                // Implicitly painting without actually invoking paint()
-                window.requestRepaintRequests();
-
-                // If the window is invisible send a blank page
-                return null;
-            }
-        }
-
-        return window;
-    }
-
-    /**
-     * Gets relative location of a theme resource.
-     * 
-     * @param theme
-     *            the Theme name.
-     * @param resource
-     *            the Theme resource.
-     * @return External URI specifying the resource
-     */
-    public String getResourceLocation(String theme, ThemeResource resource) {
-
-        if (resourcePath == null) {
-            return resource.getResourceId();
+            throw new ServletException("getNewApplication failed", e);
         }
-        return resourcePath + theme + "/" + resource.getResourceId();
     }
 
-    /**
-     * Implementation of ParameterHandler.ErrorEvent interface.
-     */
-    public class ParameterHandlerErrorImpl implements
-            ParameterHandler.ErrorEvent, Serializable {
-
-        private ParameterHandler owner;
-
-        private Throwable throwable;
-
-        /**
-         * Gets the contained throwable.
-         * 
-         * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable()
-         */
-        public Throwable getThrowable() {
-            return throwable;
-        }
-
-        /**
-         * Gets the source ParameterHandler.
-         * 
-         * @see com.itmill.toolkit.terminal.ParameterHandler.ErrorEvent#getParameterHandler()
-         */
-        public ParameterHandler getParameterHandler() {
-            return owner;
-        }
-
-    }
-
-    /**
-     * Implementation of URIHandler.ErrorEvent interface.
-     */
-    public class URIHandlerErrorImpl implements URIHandler.ErrorEvent,
-            Serializable {
-
-        private final URIHandler owner;
-
-        private final Throwable throwable;
-
-        /**
-         * 
-         * @param owner
-         * @param throwable
-         */
-        private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) {
-            this.owner = owner;
-            this.throwable = throwable;
-        }
-
-        /**
-         * Gets the contained throwable.
-         * 
-         * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable()
-         */
-        public Throwable getThrowable() {
-            return throwable;
-        }
-
-        /**
-         * Gets the source URIHandler.
-         * 
-         * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler()
-         */
-        public URIHandler getURIHandler() {
-            return owner;
-        }
-    }
-
-    /**
-     * Gets resource path using different implementations. Required to
-     * supporting different servlet container implementations (application
-     * servers).
-     * 
-     * @param servletContext
-     * @param path
-     *            the resource path.
-     * @return the resource path.
-     */
-    protected static String getResourcePath(ServletContext servletContext,
-            String path) {
-        String resultPath = null;
-        resultPath = servletContext.getRealPath(path);
-        if (resultPath != null) {
-            return resultPath;
-        } else {
-            try {
-                final URL url = servletContext.getResource(path);
-                resultPath = url.getFile();
-            } catch (final Exception e) {
-                // FIXME: Handle exception
-                e.printStackTrace();
-            }
-        }
-        return resultPath;
-    }
-
-    public class RequestError implements Terminal.ErrorEvent, Serializable {
-
-        private final Throwable throwable;
-
-        public RequestError(Throwable throwable) {
-            this.throwable = throwable;
-        }
-
-        public Throwable getThrowable() {
-            return throwable;
-        }
-
-    }
-
-    /**
-     * Returns true if the servlet is running in production mode. Production
-     * mode disables all debug facilities.
-     * 
-     * @return true if in production mode, false if in debug mode
-     */
-    public boolean isProductionMode() {
-        return productionMode;
+    @Override
+    protected Class<? extends Application> getApplicationClass() {
+        return applicationClass;
     }
-
 }
\ No newline at end of file
index 033284259d52e93b685059fd4e74c9df21451f0e..ced5ba119856b946f3387d263e7642364695e46c 100644 (file)
@@ -92,13 +92,13 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
 
     public static final String VAR_ARRAYITEM_SEPARATOR = "\u001c";
 
-    private final HashSet currentlyOpenWindowsInClient = new HashSet();
+    private final HashSet<String> currentlyOpenWindowsInClient = new HashSet<String>();
 
     private static final int MAX_BUFFER_SIZE = 64 * 1024;
 
     private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts";
 
-    private final ArrayList dirtyPaintabletSet = new ArrayList();
+    private final ArrayList<Paintable> dirtyPaintabletSet = new ArrayList<Paintable>();
 
     private final HashMap<Paintable, String> paintableIdMap = new HashMap<Paintable, String>();
 
@@ -106,7 +106,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
 
     private int idSequence = 0;
 
-    private final ApplicationServlet applicationServlet;
+    private final AbstractApplicationServlet applicationServlet;
 
     private final Application application;
 
@@ -114,14 +114,14 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
     // thus should be thread-safe.
     private String closingWindowName = null;
 
-    private List locales;
+    private List<String> locales;
 
     private int pendingLocalesIndex;
 
     private int timeoutInterval = -1;
 
     public CommunicationManager(Application application,
-            ApplicationServlet applicationServlet) {
+            AbstractApplicationServlet applicationServlet) {
         this.application = application;
         requireLocale(application.getLocale().toString());
         this.applicationServlet = applicationServlet;
@@ -228,9 +228,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
      * @throws ServletException
      */
     public void handleUidlRequest(HttpServletRequest request,
-            HttpServletResponse response, ApplicationServlet applicationServlet)
-            throws IOException, ServletException,
-            InvalidUIDLSecurityKeyException {
+            HttpServletResponse response,
+            AbstractApplicationServlet applicationServlet) throws IOException,
+            ServletException, InvalidUIDLSecurityKeyException {
 
         // repaint requested or session has timed out and new one is created
         boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null)
@@ -317,7 +317,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
 
     private void paintAfterVariablechanges(HttpServletRequest request,
             HttpServletResponse response,
-            ApplicationServlet applicationServlet, boolean repaintAll,
+            AbstractApplicationServlet applicationServlet, boolean repaintAll,
             final PrintWriter outWriter, Window window, boolean analyzeLayouts)
             throws IOException, ServletException, PaintException {
 
@@ -394,8 +394,8 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
                 // We need to avoid painting children before parent.
                 // This is ensured by ordering list by depth in component
                 // tree
-                Collections.sort(paintables, new Comparator() {
-                    public int compare(Object o1, Object o2) {
+                Collections.sort(paintables, new Comparator<Paintable>() {
+                    public int compare(Paintable o1, Paintable o2) {
                         Component c1 = (Component) o1;
                         Component c2 = (Component) o2;
                         int d1 = 0;
@@ -885,8 +885,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
         outWriter.print(", \"locales\":[");
         for (; pendingLocalesIndex < locales.size(); pendingLocalesIndex++) {
 
-            final Locale l = generateLocale((String) locales
-                    .get(pendingLocalesIndex));
+            final Locale l = generateLocale(locales.get(pendingLocalesIndex));
             // Locale name
             outWriter.print("{\"name\":\"" + l.toString() + "\",");
 
@@ -1034,12 +1033,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
         if (window == null) {
 
             // Get the path from URL
-            String path = request.getPathInfo();
-            if (applicationServlet.isApplicationRunnerServlet) {
-                path = path
-                        .substring(1 + applicationServlet.applicationRunnerClassname
-                                .length());
-            }
+            String path = applicationServlet.getRequestPathInfo(request);
             path = path.substring("/UIDL".length());
 
             // If the path is specified, create name from it
@@ -1175,8 +1169,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
      *            root window for which dirty components is to be fetched
      * @return
      */
-    private ArrayList getDirtyVisibleComponents(Window w) {
-        final ArrayList resultset = new ArrayList(dirtyPaintabletSet);
+    private ArrayList<Paintable> getDirtyVisibleComponents(Window w) {
+        final ArrayList<Paintable> resultset = new ArrayList<Paintable>(
+                dirtyPaintabletSet);
 
         // The following algorithm removes any components that would be painted
         // as a direct descendant of other components from the dirty components
@@ -1363,7 +1358,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
 
     public void requireLocale(String value) {
         if (locales == null) {
-            locales = new ArrayList();
+            locales = new ArrayList<String>();
             locales.add(application.getLocale().toString());
             pendingLocalesIndex = 0;
         }
@@ -1452,7 +1447,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
     DownloadStream handleURI(Window window, HttpServletRequest request,
             HttpServletResponse response) {
 
-        String uri = request.getPathInfo();
+        String uri = applicationServlet.getRequestPathInfo(request);
 
         // If no URI is available
         if (uri == null) {
@@ -1464,18 +1459,6 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
             }
         }
 
-        // If using application runner, remove package and class name
-        if (applicationServlet.isApplicationRunnerServlet) {
-            if (uri.contains("/")) {
-                uri = uri
-                        .replaceFirst(
-                                applicationServlet.applicationRunnerClassname
-                                        + "/", "");
-            } else {
-                uri = "";
-            }
-        }
-
         // Handles the uri
         try {
             URL context = application.getURL();
index 0193b4d3d77c84a67e103f1f0d42530d71756b69..4f2ca1817f3e2a96fdf5940b2b0cb17dc659f228 100644 (file)
@@ -36,11 +36,11 @@ import com.itmill.toolkit.service.ApplicationContext;
 public class WebApplicationContext implements ApplicationContext,
         HttpSessionBindingListener, Serializable {
 
-    protected List listeners;
+    protected List<TransactionListener> listeners;
 
     protected transient HttpSession session;
 
-    protected final HashSet applications = new HashSet();
+    protected final HashSet<Application> applications = new HashSet<Application>();
 
     protected WebBrowser browser = new WebBrowser();
 
@@ -131,7 +131,7 @@ public class WebApplicationContext implements ApplicationContext,
      */
     public void addTransactionListener(TransactionListener listener) {
         if (listeners == null) {
-            listeners = new LinkedList();
+            listeners = new LinkedList<TransactionListener>();
         }
         listeners.add(listener);
     }
@@ -179,14 +179,14 @@ public class WebApplicationContext implements ApplicationContext,
             return;
         }
 
-        LinkedList exceptions = null;
+        LinkedList<Exception> exceptions = null;
         for (final Iterator i = listeners.iterator(); i.hasNext();) {
             try {
                 ((ApplicationContext.TransactionListener) i.next())
                         .transactionEnd(application, request);
             } catch (final RuntimeException t) {
                 if (exceptions == null) {
-                    exceptions = new LinkedList();
+                    exceptions = new LinkedList<Exception>();
                 }
                 exceptions.add(t);
             }
@@ -232,8 +232,7 @@ public class WebApplicationContext implements ApplicationContext,
         // closing
         try {
             while (!applications.isEmpty()) {
-                final Application app = (Application) applications.iterator()
-                        .next();
+                final Application app = applications.iterator().next();
                 app.close();
                 applicationToAjaxAppMgrMap.remove(app);
                 removeApplication(app);
@@ -266,13 +265,14 @@ public class WebApplicationContext implements ApplicationContext,
     /**
      * Gets communication manager for an application.
      * 
-     * If this application has not been running before, new manager is created.
+     * If this application has not been running before, a new manager is
+     * created.
      * 
      * @param application
      * @return CommunicationManager
      */
     protected CommunicationManager getApplicationManager(
-            Application application, ApplicationServlet servlet) {
+            Application application, AbstractApplicationServlet servlet) {
         CommunicationManager mgr = applicationToAjaxAppMgrMap.get(application);
 
         if (mgr == null) {