summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJouni Koivuviita <jouni.koivuviita@itmill.com>2009-05-06 11:14:08 +0000
committerJouni Koivuviita <jouni.koivuviita@itmill.com>2009-05-06 11:14:08 +0000
commitd31cebd06e5e9c8fccaa437b9514a489bb286054 (patch)
tree653559a785ef540c8492edf4ad6d77ca49790c33 /src
parentaf59c6a464aab31d166215c24b6055305063f728 (diff)
downloadvaadin-framework-d31cebd06e5e9c8fccaa437b9514a489bb286054.tar.gz
vaadin-framework-d31cebd06e5e9c8fccaa437b9514a489bb286054.zip
Merges from 6.0 branch.
svn changeset:7637/svn branch:theme_2009_03
Diffstat (limited to 'src')
-rw-r--r--src/com/itmill/toolkit/launcher/WidgetsetCompiler.java55
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/BrowserInfo.java32
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ICaption.java6
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java49
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/ITabsheet.java23
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/server/AbstractApplicationServlet.java1797
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/server/ApplicationRunnerServlet.java173
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java1676
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java53
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java18
10 files changed, 2168 insertions, 1714 deletions
diff --git a/src/com/itmill/toolkit/launcher/WidgetsetCompiler.java b/src/com/itmill/toolkit/launcher/WidgetsetCompiler.java
new file mode 100644
index 0000000000..389aacaee3
--- /dev/null
+++ b/src/com/itmill/toolkit/launcher/WidgetsetCompiler.java
@@ -0,0 +1,55 @@
+package com.itmill.toolkit.launcher;
+
+import com.google.gwt.dev.GWTCompiler;
+
+/**
+ * A wrapper for the GWT 1.6 compiler that runs the compiler in a new thread.
+ *
+ * This allows circumventing a J2SE 5.0 bug (6316197) that prevents setting the
+ * stack size for the main thread. Thus, larger widgetsets can be compiled.
+ *
+ * This class takes the same command line arguments as the
+ * com.google.gwt.dev.GWTCompiler class. The old and deprecated compiler is used
+ * for compatibility with GWT 1.5.
+ *
+ * A typical invocation would use e.g. the following arguments
+ *
+ * "-out WebContent/ITMILL/widgetsets com.itmill.toolkit.terminal.gwt.DefaultWidgetSet"
+ *
+ * In addition, larger memory usage settings for the VM should be used, e.g.
+ *
+ * "-Xms256M -Xmx512M -Xss8M"
+ *
+ * The source directory containing widgetset and related classes must be
+ * included in the classpath, as well as the gwt-dev-[platform].jar and other
+ * relevant JARs.
+ */
+public class WidgetsetCompiler {
+
+ /**
+ * @param args
+ * same arguments as for com.google.gwt.dev.Compiler
+ */
+ public static void main(final String[] args) {
+ try {
+ // run the compiler in a different thread to enable using the
+ // user-set stack size
+
+ // on Windows, the default stack size is too small for the main
+ // thread and cannot be changed in JRE 1.5 (see
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6316197)
+
+ Runnable runCompiler = new Runnable() {
+ public void run() {
+ GWTCompiler.main(args);
+ }
+ };
+ Thread runThread = new Thread(runCompiler);
+ runThread.start();
+ runThread.join();
+ System.out.println("Widgetset compilation finished");
+ } catch (Throwable thr) {
+ thr.printStackTrace();
+ }
+ }
+}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/BrowserInfo.java b/src/com/itmill/toolkit/terminal/gwt/client/BrowserInfo.java
index 42e44d0374..1604ac65d1 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/BrowserInfo.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/BrowserInfo.java
@@ -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());
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java b/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java
index d3e12769ad..7487ecc434 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java
@@ -436,4 +436,8 @@ public class ICaption extends HTML {
}
}
-}
+ protected Element getTextElement() {
+ return captionText;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java
index 8b10854959..a64a41fa7f 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java
@@ -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() {
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/ITabsheet.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/ITabsheet.java
index 519ec6034a..e40b6ac0a8 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ui/ITabsheet.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/ITabsheet.java
@@ -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
index 0000000000..963550d08c
--- /dev/null
+++ b/src/com/itmill/toolkit/terminal/gwt/server/AbstractApplicationServlet.java
@@ -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
index 0000000000..1932e0adc9
--- /dev/null
+++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationRunnerServlet.java
@@ -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;
+ }
+
+}
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java
index 17b6c66c45..bf26df9c5c 100644
--- a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java
+++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java
@@ -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
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java
index 033284259d..ced5ba1198 100644
--- a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java
+++ b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java
@@ -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();
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java b/src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java
index 0193b4d3d7..4f2ca1817f 100644
--- a/src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java
+++ b/src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java
@@ -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) {