123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896 |
- /* *************************************************************************
-
- IT Mill Toolkit
-
- Development of Browser User Interfaces Made Easy
-
- Copyright (C) 2000-2006 IT Mill Ltd
-
- *************************************************************************
-
- This product is distributed under commercial license that can be found
- from the product package on license.pdf. Use of this product might
- require purchasing a commercial license from IT Mill Ltd. For guidelines
- on usage, see licensing-guidelines.html
-
- *************************************************************************
-
- For more information, contact:
-
- IT Mill Ltd phone: +358 2 4802 7180
- Ruukinkatu 2-4 fax: +358 2 4802 7181
- 20540, Turku email: info@itmill.com
- Finland company www: www.itmill.com
-
- Primary source for information and releases: www.itmill.com
-
- ********************************************************************** */
-
- package com.itmill.toolkit.terminal.web;
-
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Date;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Set;
- import java.util.StringTokenizer;
- import java.util.Vector;
- import java.util.WeakHashMap;
-
- import javax.servlet.ServletContext;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionBindingEvent;
- import javax.servlet.http.HttpSessionBindingListener;
-
- import org.xml.sax.SAXException;
-
- import com.itmill.toolkit.Application;
- import com.itmill.toolkit.Application.WindowAttachEvent;
- import com.itmill.toolkit.Application.WindowDetachEvent;
- import com.itmill.toolkit.service.FileTypeResolver;
- import com.itmill.toolkit.service.License;
- import com.itmill.toolkit.service.License.InvalidLicenseFile;
- import com.itmill.toolkit.service.License.LicenseFileHasAlreadyBeenRead;
- import com.itmill.toolkit.service.License.LicenseFileHasNotBeenRead;
- import com.itmill.toolkit.service.License.LicenseSignatureIsInvalid;
- import com.itmill.toolkit.service.License.LicenseViolation;
- import com.itmill.toolkit.terminal.DownloadStream;
- import com.itmill.toolkit.terminal.Paintable;
- import com.itmill.toolkit.terminal.ParameterHandler;
- import com.itmill.toolkit.terminal.ThemeResource;
- import com.itmill.toolkit.terminal.URIHandler;
- import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent;
- import com.itmill.toolkit.terminal.web.ThemeSource.ThemeException;
- import com.itmill.toolkit.ui.Window;
-
- /**
- * This servlet connects IT Mill Toolkit Application to Web. This servlet
- * replaces both WebAdapterServlet and AjaxAdapterServlet.
- *
- * @author IT Mill Ltd.
- * @version
- * @VERSION@
- * @since 4.0
- */
-
- public class ApplicationServlet extends HttpServlet implements
- Application.WindowAttachListener, Application.WindowDetachListener,
- Paintable.RepaintRequestListener {
-
- private static final long serialVersionUID = -4937882979845826574L;
-
- /** Version number of this release. For example "4.0.0" */
- public static final String VERSION;
-
- /** Major version number. For example 4 in 4.1.0. */
- public static final int VERSION_MAJOR;
-
- /** Minor version number. For example 1 in 4.1.0. */
- public static final int VERSION_MINOR;
-
- /** Build number. For example 0-beta1 in 4.0.0-beta1. */
- public static final String VERSION_BUILD;
-
- /* Initialize version numbers from string replaced by build-script. */
- static {
- if ("@VERSION@".equals("@" + "VERSION" + "@"))
- VERSION = "4.0.0-INTERNAL-NONVERSIONED-DEBUG-BUILD";
- else
- VERSION = "@VERSION@";
- String[] digits = VERSION.split("\\.");
- VERSION_MAJOR = Integer.parseInt(digits[0]);
- VERSION_MINOR = Integer.parseInt(digits[1]);
- VERSION_BUILD = digits[2];
- }
-
- // Configurable parameter names
- private static final String PARAMETER_DEBUG = "Debug";
-
- private static final String PARAMETER_DEFAULT_THEME_JAR = "DefaultThemeJar";
-
- private static final String PARAMETER_THEMESOURCE = "ThemeSource";
-
- private static final String PARAMETER_THEME_CACHETIME = "ThemeCacheTime";
-
- private static final String PARAMETER_MAX_TRANSFORMERS = "MaxTransformers";
-
- private static final String PARAMETER_TRANSFORMER_CACHETIME = "TransformerCacheTime";
-
- private static final int DEFAULT_THEME_CACHETIME = 1000 * 60 * 60 * 24;
-
- private static final int DEFAULT_BUFFER_SIZE = 32 * 1024;
-
- private static final int DEFAULT_MAX_TRANSFORMERS = 1;
-
- private static final int MAX_BUFFER_SIZE = 64 * 1024;
-
- private static final String SESSION_ATTR_VARMAP = "itmill-toolkit-varmap";
-
- private static final String SESSION_ATTR_CONTEXT = "itmill-toolkit-context";
-
- protected static final String SESSION_ATTR_APPS = "itmill-toolkit-apps";
-
- private static final String SESSION_BINDING_LISTENER = "itmill-toolkit-bindinglistener";
-
- // TODO Should default or base theme be the default?
- protected static final String DEFAULT_THEME = "base";
-
- private static final String RESOURCE_URI = "/RES/";
-
- private static final String AJAX_UIDL_URI = "/UIDL/";
-
- private static final String THEME_DIRECTORY_PATH = "WEB-INF/lib/themes/";
-
- private static final String THEME_LISTING_FILE = THEME_DIRECTORY_PATH
- + "themes.txt";
-
- private static final String DEFAULT_THEME_JAR_PREFIX = "itmill-toolkit-themes";
-
- private static final String DEFAULT_THEME_JAR = "WEB-INF/lib/"
- + DEFAULT_THEME_JAR_PREFIX + "-" + VERSION + ".jar";
-
- private static final String DEFAULT_THEME_TEMP_FILE_PREFIX = "ITMILL_TMP_";
-
- private static final String SERVER_COMMAND_PARAM = "SERVER_COMMANDS";
-
- private static final int SERVER_COMMAND_STREAM_MAINTAIN_PERIOD = 15000;
-
- private static final int SERVER_COMMAND_HEADER_PADDING = 2000;
-
- // Maximum delay between request for an user to be considered active (in ms)
- private static final long ACTIVE_USER_REQUEST_INTERVAL = 1000 * 45;
-
- // Private fields
- private Class applicationClass;
-
- private Properties applicationProperties;
-
- private UIDLTransformerFactory transformerFactory;
-
- private CollectionThemeSource themeSource;
-
- private String resourcePath = null;
-
- private String debugMode = "";
-
- private int maxConcurrentTransformers;
-
- private long transformerCacheTime;
-
- private long themeCacheTime;
-
- private WeakHashMap applicationToDirtyWindowSetMap = new WeakHashMap();
-
- private WeakHashMap applicationToServerCommandStreamLock = new WeakHashMap();
-
- private static WeakHashMap applicationToLastRequestDate = new WeakHashMap();
-
- private List allWindows = new LinkedList();
-
- private WeakHashMap applicationToAjaxAppMgrMap = new WeakHashMap();
-
- private HashMap licenseForApplicationClass = new HashMap();
-
- private static HashSet licensePrintedForApplicationClass = new HashSet();
-
- /**
- * Called by the servlet container to indicate to a servlet that the servlet
- * is being placed into service.
- *
- * @param servletConfig
- * object containing the servlet's configuration and
- * initialization parameters
- * @throws ServletException
- * if an exception has occurred that interferes with the
- * servlet's normal operation.
- */
- public void init(javax.servlet.ServletConfig servletConfig)
- throws javax.servlet.ServletException {
- super.init(servletConfig);
-
- // Get the application class name
- String applicationClassName = servletConfig
- .getInitParameter("application");
- if (applicationClassName == null) {
- Log.error("Application not specified in servlet parameters");
- }
-
- // Store the application parameters into Properties object
- this.applicationProperties = new Properties();
- for (Enumeration e = servletConfig.getInitParameterNames(); e
- .hasMoreElements();) {
- String name = (String) e.nextElement();
- this.applicationProperties.setProperty(name, servletConfig
- .getInitParameter(name));
- }
-
- // Override with server.xml parameters
- ServletContext context = servletConfig.getServletContext();
- for (Enumeration e = context.getInitParameterNames(); e
- .hasMoreElements();) {
- String name = (String) e.nextElement();
- this.applicationProperties.setProperty(name, context
- .getInitParameter(name));
- }
-
- // Get the debug window parameter
- String debug = getApplicationOrSystemProperty(PARAMETER_DEBUG, "")
- .toLowerCase();
- // Enable application specific debug
- if (!"".equals(debug) && !"true".equals(debug)
- && !"false".equals(debug))
- throw new ServletException(
- "If debug parameter is given for an application, it must be 'true' or 'false'");
- this.debugMode = debug;
-
- // Get the maximum number of simultaneous transformers
- this.maxConcurrentTransformers = Integer
- .parseInt(getApplicationOrSystemProperty(
- PARAMETER_MAX_TRANSFORMERS, "-1"));
- if (this.maxConcurrentTransformers < 1)
- this.maxConcurrentTransformers = DEFAULT_MAX_TRANSFORMERS;
-
- // Get cache time for transformers
- this.transformerCacheTime = Integer
- .parseInt(getApplicationOrSystemProperty(
- PARAMETER_TRANSFORMER_CACHETIME, "-1")) * 1000;
-
- // Get cache time for theme resources
- this.themeCacheTime = Integer.parseInt(getApplicationOrSystemProperty(
- PARAMETER_THEME_CACHETIME, "-1")) * 1000;
- if (this.themeCacheTime < 0) {
- this.themeCacheTime = DEFAULT_THEME_CACHETIME;
- }
-
- // Add all specified theme sources
- this.themeSource = new CollectionThemeSource();
- List directorySources = getThemeSources();
- for (Iterator i = directorySources.iterator(); i.hasNext();) {
- this.themeSource.add((ThemeSource) i.next());
- }
-
- // Add the default theme source
- String[] defaultThemeFiles = new String[] { getApplicationOrSystemProperty(
- PARAMETER_DEFAULT_THEME_JAR, DEFAULT_THEME_JAR) };
- File f = findDefaultThemeJar(defaultThemeFiles);
- try {
- // Add themes.jar if exists
- if (f != null && f.exists())
- this.themeSource.add(new JarThemeSource(f, this, ""));
- else {
- Log.warn("Default theme JAR not found in: "
- + Arrays.asList(defaultThemeFiles));
- }
-
- } catch (Exception e) {
- throw new ServletException("Failed to load default theme from "
- + Arrays.asList(defaultThemeFiles), e);
- }
-
- // Check that at least one themesource was loaded
- if (this.themeSource.getThemes().size() <= 0) {
- throw new ServletException(
- "No themes found in specified themesources.");
- }
-
- // Initialize the transformer factory, if not initialized
- if (this.transformerFactory == null) {
-
- this.transformerFactory = new UIDLTransformerFactory(
- this.themeSource, this, this.maxConcurrentTransformers,
- this.transformerCacheTime);
- }
-
- // Load the application class using the same class loader
- // as the servlet itself
- ClassLoader loader = this.getClass().getClassLoader();
- try {
- this.applicationClass = loader.loadClass(applicationClassName);
- } catch (ClassNotFoundException e) {
- throw new ServletException("Failed to load application class: "
- + applicationClassName);
- }
- }
-
- /**
- * Get an application or system property value.
- *
- * @param parameterName
- * Name or the parameter
- * @param defaultValue
- * Default to be used
- * @return String value or default if not found
- */
- private String getApplicationOrSystemProperty(String parameterName,
- String defaultValue) {
-
- // Try application properties
- String val = this.applicationProperties.getProperty(parameterName);
- if (val != null) {
- return val;
- }
-
- // Try lowercased application properties for backward compability with
- // 3.0.2 and earlier
- val = this.applicationProperties.getProperty(parameterName
- .toLowerCase());
- if (val != null) {
- return val;
- }
-
- // Try system properties
- String pkgName;
- Package pkg = this.getClass().getPackage();
- if (pkg != null) {
- pkgName = pkg.getName();
- } else {
- String clazzName = this.getClass().getName();
- pkgName = new String(clazzName.toCharArray(), 0, clazzName
- .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;
- }
-
- /**
- * Get ThemeSources from given path. Construct the list of avalable themes
- * in path using the following sources: 1. content of THEME_PATH directory
- * (if available) 2. The themes listed in THEME_LIST_FILE 3. "themesource"
- * application parameter - "ThemeSource" system
- * property
- *
- * @param THEME_DIRECTORY_PATH
- * @return List
- */
- private List getThemeSources() throws ServletException {
-
- List returnValue = new LinkedList();
-
- // Check the list file in theme directory
- List sourcePaths = new LinkedList();
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(
- this.getServletContext().getResourceAsStream(
- THEME_LISTING_FILE)));
- String line = null;
- while ((line = reader.readLine()) != null) {
- sourcePaths.add(THEME_DIRECTORY_PATH + line.trim());
- }
- if (this.isDebugMode(null)) {
- Log.debug("Listed " + sourcePaths.size() + " themes in "
- + THEME_LISTING_FILE + ". Loading " + sourcePaths);
- }
- } catch (Exception ignored) {
- // If the file reading fails, just skip to next method
- }
-
- // If no file was found or it was empty,
- // try to add themes filesystem directory if it is accessible
- if (sourcePaths.size() <= 0) {
- if (this.isDebugMode(null)) {
- Log.debug("No themes listed in " + THEME_LISTING_FILE
- + ". Trying to read the content of directory "
- + THEME_DIRECTORY_PATH);
- }
-
- try {
- String path = this.getServletContext().getRealPath(
- THEME_DIRECTORY_PATH);
- if (path != null) {
- File f = new File(path);
- if (f != null && f.exists())
- returnValue.add(new DirectoryThemeSource(f, this));
- }
- } catch (java.io.IOException je) {
- Log.info("Theme directory " + THEME_DIRECTORY_PATH
- + " not available. Skipped.");
- } catch (ThemeException e) {
- throw new ServletException("Failed to load themes from "
- + THEME_DIRECTORY_PATH, e);
- }
- }
-
- // Add the theme sources from application properties
- String paramValue = getApplicationOrSystemProperty(
- PARAMETER_THEMESOURCE, null);
- if (paramValue != null) {
- StringTokenizer st = new StringTokenizer(paramValue, ";");
- while (st.hasMoreTokens()) {
- sourcePaths.add(st.nextToken());
- }
- }
-
- // Construct appropriate theme source instances for each path
- for (Iterator i = sourcePaths.iterator(); i.hasNext();) {
- String source = (String) i.next();
- File sourceFile = new File(source);
- try {
-
- // Relative files are treated as streams (to support
- // resource inside WAR files)
- if (!sourceFile.isAbsolute()) {
- returnValue.add(new ServletThemeSource(this
- .getServletContext(), this, source));
- } else if (sourceFile.isDirectory()) {
-
- // Absolute directories are read from filesystem
- returnValue.add(new DirectoryThemeSource(sourceFile, this));
- } else {
-
- // Absolute JAR-files are read from filesystem
- returnValue.add(new JarThemeSource(sourceFile, this, ""));
- }
- } catch (Exception e) {
- // Any exception breaks the the init
- throw new ServletException("Invalid theme source: " + source, e);
- }
- }
-
- // Return the constructed list of theme sources
- return returnValue;
- }
-
- /**
- * Receives standard HTTP requests from the public service method and
- * dispatches them.
- *
- * @param request
- * object that contains the request the client made of the
- * servlet
- * @param response
- * 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
- */
- protected void service(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
-
- // Transformer and output stream for the result
- UIDLTransformer transformer = null;
- HttpVariableMap variableMap = null;
- OutputStream out = response.getOutputStream();
- HashSet currentlyDirtyWindowsForThisApplication = new HashSet();
- Application application = null;
- try {
-
- // Handle resource requests
- if (handleResourceRequest(request, response))
- return;
-
- // Handle server commands
- if (handleServerCommands(request, response))
- return;
-
- // Get the application
- application = getApplication(request);
-
- // Create application if it doesn't exist
- if (application == null)
- application = createApplication(request);
-
- // Set the last application request date
- applicationToLastRequestDate.put(application, new Date());
-
- // Invoke context transaction listeners
- ((WebApplicationContext) application.getContext())
- .startTransaction(application, request);
-
- // Is this a download request from application
- DownloadStream download = null;
-
- // The rest of the process is synchronized with the application
- // in order to guarantee that no parallel variable handling is
- // made
- synchronized (application) {
-
- // Handle UIDL requests?
- String resourceId = request.getPathInfo();
- if (resourceId != null && resourceId.startsWith(AJAX_UIDL_URI)) {
-
- getApplicationManager(application).handleUidlRequest(
- request, response);
-
- return;
- }
-
- // Get the variable map
- variableMap = getVariableMap(application, request);
- if (variableMap == null)
- return;
-
- // Change all variables based on request parameters
- Map unhandledParameters = variableMap.handleVariables(request,
- application);
-
- // Check/handle client side feature checks
- WebBrowserProbe
- .handleProbeRequest(request, unhandledParameters);
-
- // Handle the URI if the application is still running
- if (application.isRunning())
- download = handleURI(application, request, response);
-
- // If this is not a download request
- if (download == null) {
-
- // Window renders are not cacheable
- response.setHeader("Cache-Control", "no-cache");
- response.setHeader("Pragma", "no-cache");
- response.setDateHeader("Expires", 0);
-
- // Find the window within the application
- Window window = null;
- if (application.isRunning())
- window = getApplicationWindow(request, application,
- unhandledParameters);
-
- // Handle the unhandled parameters if the application is
- // still running
- if (window != null && unhandledParameters != null
- && !unhandledParameters.isEmpty()) {
- try {
- window.handleParameters(unhandledParameters);
- } catch (Throwable t) {
- application
- .terminalError(new ParameterHandlerErrorImpl(
- window, t));
- }
- }
-
- // Remove application if it has stopped
- if (!application.isRunning()) {
- endApplication(request, response, application);
- return;
- }
-
- // Return blank page, if no window found
- if (window == null) {
- response.setContentType("text/html");
- BufferedWriter page = new BufferedWriter(
- new OutputStreamWriter(out));
- page.write("<html><head><script>");
- page
- .write(ThemeFunctionLibrary
- .generateWindowScript(
- null,
- application,
- this,
- WebBrowserProbe
- .getTerminalType(request
- .getSession())));
- page.write("</script></head><body>");
- page
- .write("The requested window has been removed from application.");
- page.write("</body></html>");
- page.close();
-
- return;
- }
-
- // Get the terminal type for the window
- WebBrowser terminalType = (WebBrowser) window.getTerminal();
-
- // Set terminal type for the window, if not already set
- if (terminalType == null) {
- terminalType = WebBrowserProbe.getTerminalType(request
- .getSession());
- window.setTerminal(terminalType);
- }
-
- // Find theme
- String themeName = window.getTheme() != null ? window
- .getTheme() : DEFAULT_THEME;
- if (unhandledParameters.get("theme") != null) {
- themeName = (String) ((Object[]) unhandledParameters
- .get("theme"))[0];
- }
- Theme theme = themeSource
- .getThemeByName(themeName);
- if (theme == null)
- throw new ServletException("Theme (named '"
- + themeName + "') can not be found");
-
- // If ajax rendering mode is preferred, a page for it is
- // rendered
- String renderingMode = theme.getPreferredMode(terminalType,
- themeSource);
- if (unhandledParameters.get("renderingMode") != null)
- renderingMode = (String) ((Object[]) unhandledParameters
- .get("renderingMode"))[0];
- if (Theme.MODE_AJAX.equals(renderingMode)
- && !(window instanceof DebugWindow)) {
- writeAjaxPage(request, response, out, unhandledParameters, window, terminalType, theme);
- return;
- }
-
- // If other than html or ajax mode is requested
- if (!Theme.MODE_HTML.equals(renderingMode)
- && !(window instanceof DebugWindow)) {
- // TODO More informal message should be given is browser
- // is not supported
- response.setContentType("text/html");
- BufferedWriter page = new BufferedWriter(
- new OutputStreamWriter(out));
- page.write("<html><head></head><body>");
- page.write("Unsupported browser.");
- page.write("</body></html>");
- page.close();
-
- return;
- }
-
- // Initialize Transformer
- UIDLTransformerType transformerType = new UIDLTransformerType(
- terminalType, theme);
-
- transformer = this.transformerFactory
- .getTransformer(transformerType);
-
- // Set the response type
- response.setContentType(terminalType.getContentType());
-
- // Create UIDL writer
- WebPaintTarget paintTarget = transformer
- .getPaintTarget(variableMap);
-
- // Assure that the correspoding debug window will be
- // repainted property
- // by clearing it before the actual paint.
- DebugWindow debugWindow = (DebugWindow) application
- .getWindow(DebugWindow.WINDOW_NAME);
- if (debugWindow != null && debugWindow != window) {
- debugWindow.setWindowUIDL(window, "Painting...");
- }
-
- // Paint window
- window.paint(paintTarget);
- paintTarget.close();
-
- // For exception handling, memorize the current dirty status
- Collection dirtyWindows = (Collection) applicationToDirtyWindowSetMap
- .get(application);
- if (dirtyWindows == null) {
- dirtyWindows = new HashSet();
- applicationToDirtyWindowSetMap.put(application,
- dirtyWindows);
- }
- currentlyDirtyWindowsForThisApplication
- .addAll(dirtyWindows);
-
- // Window is now painted
- windowPainted(application, window);
-
- // Debug
- if (debugWindow != null && debugWindow != window) {
- debugWindow
- .setWindowUIDL(window, paintTarget.getUIDL());
- }
-
- // Set the function library state for this thread
- ThemeFunctionLibrary.setState(application, window,
- transformerType.getWebBrowser(), request
- .getSession(), this, transformerType
- .getTheme().getName());
-
- }
- }
-
- // For normal requests, transform the window
- if (download == null) {
-
- // Transform and output the result to browser
- // Note that the transform and transfer of the result is
- // not synchronized with the variable map. This allows
- // parallel transfers and transforms for better performance,
- // but requires that all calls from the XSL to java are
- // thread-safe
- transformer.transform(out);
- }
-
- // For download request, transfer the downloaded data
- else {
-
- handleDownload(download, request, response);
- }
-
- } catch (UIDLTransformerException te) {
-
- try {
- // Write the error report to client
- response.setContentType("text/html");
- BufferedWriter err = new BufferedWriter(new OutputStreamWriter(
- out));
- err
- .write("<html><head><title>Application Internal Error</title></head><body>");
- err.write("<h1>" + te.getMessage() + "</h1>");
- err.write(te.getHTMLDescription());
- err.write("</body></html>");
- err.close();
- } catch (Throwable t) {
- Log.except("Failed to write error page: " + t
- + ". Original exception was: ", te);
- }
-
- // Add previously dirty windows to dirtyWindowList in order
- // to make sure that eventually they are repainted
- Application currentApplication = getApplication(request);
- for (Iterator iter = currentlyDirtyWindowsForThisApplication
- .iterator(); iter.hasNext();) {
- Window dirtyWindow = (Window) iter.next();
- addDirtyWindow(currentApplication, dirtyWindow);
- }
-
- } catch (Throwable e) {
- // Re-throw other exceptions
- throw new ServletException(e);
- } finally {
-
- // Release transformer
- if (transformer != null)
- transformerFactory.releaseTransformer(transformer);
-
- // Notify transaction end
- if (application != null)
- ((WebApplicationContext) application.getContext())
- .endTransaction(application, request);
-
- // Clean the function library state for this thread
- // for security reasons
- ThemeFunctionLibrary.cleanState();
- }
- }
-
- private void writeAjaxPage(HttpServletRequest request, HttpServletResponse response, OutputStream out, Map unhandledParameters, Window window, WebBrowser terminalType, Theme theme) throws IOException, MalformedURLException {
- response.setContentType("text/html");
- BufferedWriter page = new BufferedWriter(
- new OutputStreamWriter(out));
-
- 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><head>\n<title>"
- + window.getCaption() + "</title>\n");
- page.write("<NOSCRIPT><META http-equiv=\"refresh\" content=\"0; url=?WA_NOSCRIPT=1\" /></NOSCRIPT>\n");
- Theme t = theme;
- Vector themes = new Vector();
- themes.add(t);
- while (t.getParent() != null) {
- String parentName = t.getParent();
- t = themeSource.getThemeByName(parentName);
- themes.add(t);
- }
- for (int k = themes.size() - 1; k >= 0; k--) {
- t = (Theme) themes.get(k);
- Collection files = t.getFileNames(terminalType,
- Theme.MODE_AJAX);
- for (Iterator i = files.iterator(); i.hasNext();) {
- String file = (String) i.next();
- if (file.endsWith(".css"))
- page
- .write("<link rel=\"stylesheet\" href=\""
- + getResourceLocation(t
- .getName(),
- new ThemeResource(
- file))
- + "\" type=\"text/css\" />\n");
- else if (file.endsWith(".js"))
- page
- .write("<script src=\""
- + getResourceLocation(t
- .getName(),
- new ThemeResource(
- file))
- + "\" type=\"text/javascript\"></script>\n");
- }
-
- }
-
- page.write("</head><body class=\"itmtk\">\n");
-
- page.write("<div id=\"ajax-wait\"><div>Loading...</div>\n");
-
- page.write("<div id=\"ajax-window\"></div>\n");
-
- page.write("<script language=\"JavaScript\">\n");
- String appUrl = getApplicationUrl(request).toString();
- page
- .write("var client = new itmill.Client("
- + "document.getElementById('ajax-window'),"
- + "\""
- + appUrl
- + (appUrl.endsWith("/") ? "" : "/")
- + "UIDL/"
- + "\",\""
- + resourcePath
- + ((Theme) themes
- .get(themes.size() - 1))
- .getName()
- + "/"
-
- + "client/\",document.getElementById('ajax-wait'));\n");
-
- // TODO Only current theme is registered to the client
- //for (int k = themes.size() - 1; k >= 0; k--) {
- // t = (Theme) themes.get(k);
- t = theme;
- String themeObjName = "itmill.themes." +
- t.getName().substring(0, 1)
- .toUpperCase()
- + t.getName().substring(1);
- page.write(" (new " + themeObjName + "(\""
- + resourcePath
- + t.getName()
- + "/\")).registerTo(client);\n");
- //}
-
- if (isDebugMode(unhandledParameters))
- page.write("client.debugEnabled =true;\n");
- page.write("client.start();\n");
-
- page.write("</script>\n");
-
- page.write("</body></html>\n");
- page.close();
- }
-
- /**
- * Handle 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.
- *
- * @see com.itmill.toolkit.terminal.URIHandler
- *
- * @param application
- * Application owning the URI
- * @param request
- * HTTP request instance
- * @param response
- * HTTP response to write to.
- * @return boolean True if the request was handled and further processing
- * should be suppressed, false otherwise.
- */
- private DownloadStream handleURI(Application application,
- HttpServletRequest request, HttpServletResponse response) {
-
- String uri = request.getPathInfo();
-
- // If no URI is available
- if (uri == null || uri.length() == 0 || uri.equals("/"))
- return null;
-
- // Remove the leading /
- while (uri.startsWith("/") && uri.length() > 0)
- uri = uri.substring(1);
-
- // Handle the uri
- DownloadStream stream = null;
- try {
- stream = application.handleURI(application.getURL(), uri);
- } catch (Throwable t) {
- application.terminalError(new URIHandlerErrorImpl(application, t));
- }
-
- return stream;
- }
-
- /**
- * Handle 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.
- *
- * @see com.itmill.toolkit.terminal.URIHandler
- *
- * @param application
- * Application owning the URI
- * @param request
- * HTTP request instance
- * @param response
- * HTTP response to write to.
- * @return boolean True if the request was handled and further processing
- * should be suppressed, false otherwise.
- */
- private void handleDownload(DownloadStream stream,
- HttpServletRequest request, HttpServletResponse response) {
-
- // Download from given stream
- InputStream data = stream.getStream();
- if (data != null) {
-
- // Set content type
- response.setContentType(stream.getContentType());
-
- // Set cache headers
- 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.
- Iterator i = stream.getParameterNames();
- if (i != null) {
- while (i.hasNext()) {
- String param = (String) i.next();
- response.setHeader((String) param, stream
- .getParameter(param));
- }
- }
-
- int bufferSize = stream.getBufferSize();
- if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE)
- bufferSize = DEFAULT_BUFFER_SIZE;
- byte[] buffer = new byte[bufferSize];
- int bytesRead = 0;
-
- try {
- OutputStream out = response.getOutputStream();
-
- while ((bytesRead = data.read(buffer)) > 0) {
- out.write(buffer, 0, bytesRead);
- out.flush();
- }
- out.close();
- } catch (IOException ignored) {
- }
-
- }
-
- }
-
- /**
- * Look for default theme JAR file.
- *
- * @return Jar file or null if not found.
- */
- private File findDefaultThemeJar(String[] fileList) {
-
- // Try to find the default theme JAR file based on the given path
- for (int i = 0; i < fileList.length; i++) {
- String path = this.getServletContext().getRealPath(fileList[i]);
- File file = null;
- if (path != null && (file = new File(path)).exists()) {
- return file;
- }
- }
-
- // If we do not have access to individual files, create a temporary
- // file from named resource.
- for (int i = 0; i < fileList.length; i++) {
- InputStream defaultTheme = this.getServletContext()
- .getResourceAsStream(fileList[i]);
- // Read the content to temporary file and return it
- if (defaultTheme != null) {
- return createTemporaryFile(defaultTheme, ".jar");
- }
- }
-
- // Try to find the default theme JAR file based on file naming scheme
- // NOTE: This is for backward compability with 3.0.2 and earlier.
- String path = this.getServletContext().getRealPath("/WEB-INF/lib");
- if (path != null) {
-
- File lib = new File(path);
- String[] files = lib.list();
- if (files != null) {
- for (int i = 0; i < files.length; i++) {
- if (files[i].toLowerCase().endsWith(".jar")
- && files[i].startsWith(DEFAULT_THEME_JAR_PREFIX)) {
- return new File(lib, files[i]);
- }
- }
- }
- }
-
- // If no file was found return null
- return null;
- }
-
- /**
- * Create a temporary file for given stream.
- *
- * @param stream
- * Stream to be stored into temporary file.
- * @param extension
- * File type extension
- * @return File
- */
- private File createTemporaryFile(InputStream stream, String extension) {
- File tmpFile;
- try {
- tmpFile = File.createTempFile(DEFAULT_THEME_TEMP_FILE_PREFIX,
- extension);
- FileOutputStream out = new FileOutputStream(tmpFile);
- byte[] buf = new byte[1024];
- int bytes = 0;
- while ((bytes = stream.read(buf)) > 0) {
- out.write(buf, 0, bytes);
- }
- out.close();
- } catch (IOException e) {
- System.err
- .println("Failed to create temporary file for default theme: "
- + e);
- tmpFile = null;
- }
-
- return tmpFile;
- }
-
- /**
- * Handle theme resource file requests. Resources supplied with the themes
- * are provided by the WebAdapterServlet.
- *
- * @param request
- * HTTP request
- * @param response
- * HTTP response
- * @return boolean True if the request was handled and further processing
- * should be suppressed, false otherwise.
- */
- private boolean handleResourceRequest(HttpServletRequest request,
- HttpServletResponse response) throws ServletException {
-
- // If the resource path is unassigned, initialize it
- if (resourcePath == null)
- resourcePath = request.getContextPath() + request.getServletPath()
- + RESOURCE_URI;
-
- String resourceId = request.getPathInfo();
-
- // Check if this really is a resource request
- if (resourceId == null || !resourceId.startsWith(RESOURCE_URI))
- return false;
-
- // Check the resource type
- resourceId = resourceId.substring(RESOURCE_URI.length());
- InputStream data = null;
- // Get theme resources
- try {
- data = themeSource.getResource(resourceId);
- } catch (ThemeSource.ThemeException e) {
- Log.info(e.getMessage());
- data = null;
- }
-
- // Write the response
- try {
- if (data != null) {
- response.setContentType(FileTypeResolver
- .getMIMEType(resourceId));
-
- // Use default cache time for theme resources
- if (this.themeCacheTime > 0) {
- response.setHeader("Cache-Control", "max-age="
- + this.themeCacheTime / 1000);
- response.setDateHeader("Expires", System
- .currentTimeMillis()
- + this.themeCacheTime);
- response.setHeader("Pragma", "cache"); // Required to apply
- // caching in some
- // Tomcats
- }
- // Write the data to client
- byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
- int bytesRead = 0;
- 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 (java.io.IOException e) {
- Log.info("Resource transfer failed: " + request.getRequestURI()
- + ". (" + e.getMessage() + ")");
- }
-
- return true;
- }
-
- /** Get the variable map for the session */
- private static synchronized HttpVariableMap getVariableMap(
- Application application, HttpServletRequest request) {
-
- HttpSession session = request.getSession();
-
- // Get the application to variablemap map
- Map varMapMap = (Map) session.getAttribute(SESSION_ATTR_VARMAP);
- if (varMapMap == null) {
- varMapMap = new WeakHashMap();
- session.setAttribute(SESSION_ATTR_VARMAP, varMapMap);
- }
-
- // Create a variable map, if it does not exists.
- HttpVariableMap variableMap = (HttpVariableMap) varMapMap
- .get(application);
- if (variableMap == null) {
- variableMap = new HttpVariableMap();
- varMapMap.put(application, variableMap);
- }
-
- return variableMap;
- }
-
- /** Get the current application URL from request */
- private URL getApplicationUrl(HttpServletRequest request)
- throws MalformedURLException {
-
- URL applicationUrl;
- try {
- URL reqURL = new URL((request.isSecure() ? "https://" : "http://")
- + request.getServerName() + ":" + request.getServerPort()
- + request.getRequestURI());
- String servletPath = request.getContextPath()
- + request.getServletPath();
- if (servletPath.length() == 0
- || servletPath.charAt(servletPath.length() - 1) != '/')
- servletPath = servletPath + "/";
- applicationUrl = new URL(reqURL, servletPath);
- } catch (MalformedURLException e) {
- Log.error("Error constructing application url "
- + request.getRequestURI() + " (" + e + ")");
- throw e;
- }
-
- return applicationUrl;
- }
-
- /**
- * Get the existing application for given request. Looks for application
- * instance for given request based on the requested URL.
- *
- * @param request
- * HTTP request
- * @return Application instance, or null if the URL does not map to valid
- * application.
- */
- private Application getApplication(HttpServletRequest request)
- throws MalformedURLException {
-
- // Ensure that the session is still valid
- HttpSession session = request.getSession(false);
- if (session == null)
- return null;
-
- // Get application list for the session.
- LinkedList applications = (LinkedList) session
- .getAttribute(SESSION_ATTR_APPS);
- if (applications == null)
- return null;
-
- // Search for the application (using the application URI) from the list
- Application application = null;
- for (Iterator i = applications.iterator(); i.hasNext()
- && application == null;) {
- Application a = (Application) i.next();
- String aPath = a.getURL().getPath();
- String servletPath = request.getContextPath()
- + request.getServletPath();
- if (servletPath.length() < aPath.length())
- servletPath += "/";
- if (servletPath.equals(aPath))
- application = a;
- }
-
- // Remove stopped applications from the list
- if (application != null && !application.isRunning()) {
- applications.remove(application);
- application = null;
- }
-
- return application;
- }
-
- /**
- * Create a new application.
- *
- * @return New application instance
- * @throws SAXException
- * @throws LicenseViolation
- * @throws InvalidLicenseFile
- * @throws LicenseSignatureIsInvalid
- * @throws LicenseFileHasNotBeenRead
- */
- private Application createApplication(HttpServletRequest request)
- throws MalformedURLException, InstantiationException,
- IllegalAccessException, LicenseFileHasNotBeenRead,
- LicenseSignatureIsInvalid, InvalidLicenseFile, LicenseViolation,
- SAXException {
-
- Application application = null;
-
- // Get the application url
- URL applicationUrl = getApplicationUrl(request);
-
- // Get application list.
- HttpSession session = request.getSession();
- if (session == null)
- return null;
- LinkedList applications = (LinkedList) session
- .getAttribute(SESSION_ATTR_APPS);
- if (applications == null) {
- applications = new LinkedList();
- session.setAttribute(SESSION_ATTR_APPS, applications);
- HttpSessionBindingListener sessionBindingListener = new SessionBindingListener(
- applications);
- session.setAttribute(SESSION_BINDING_LISTENER,
- sessionBindingListener);
- }
-
- // Create new application and start it
- try {
- application = (Application) this.applicationClass.newInstance();
- applications.add(application);
-
- // Listen to window add/removes (for web mode)
- application.addListener((Application.WindowAttachListener) this);
- application.addListener((Application.WindowDetachListener) this);
-
- // Set localte
- application.setLocale(request.getLocale());
-
- // Get application context for this session
- WebApplicationContext context = (WebApplicationContext) session
- .getAttribute(SESSION_ATTR_CONTEXT);
- if (context == null) {
- context = new WebApplicationContext(session);
- session.setAttribute(SESSION_ATTR_CONTEXT, context);
- }
-
- // Start application and check license
- initializeLicense(application);
- application.start(applicationUrl, this.applicationProperties,
- context);
- checkLicense(application);
-
- } catch (IllegalAccessException e) {
- Log.error("Illegal access to application class "
- + this.applicationClass.getName());
- throw e;
- } catch (InstantiationException e) {
- Log.error("Failed to instantiate application class: "
- + this.applicationClass.getName());
- throw e;
- }
-
- return application;
- }
-
- private void initializeLicense(Application application) {
-
- License license = (License) licenseForApplicationClass.get(application
- .getClass());
- if (license == null) {
- license = new License();
- licenseForApplicationClass.put(application.getClass(), license);
- }
- application.setToolkitLicense(license);
- }
-
- private void checkLicense(Application application)
- throws LicenseFileHasNotBeenRead, LicenseSignatureIsInvalid,
- InvalidLicenseFile, LicenseViolation, SAXException {
- License license = application.getToolkitLicense();
- if (!license.hasBeenRead()) {
- InputStream lis;
- try {
- lis = getServletContext().getResource(
- "/WEB-INF/itmill-toolkit-license.xml").openStream();
- license.readLicenseFile(lis);
- } catch (MalformedURLException e) {
- // This should not happen
- throw new RuntimeException(e);
- } catch (IOException e) {
- // This should not happen
- throw new RuntimeException(e);
- } catch (LicenseFileHasAlreadyBeenRead e) {
- // This should not happen
- throw new RuntimeException(e);
- }
- }
-
- // For each application class, print license description - once
- if (!licensePrintedForApplicationClass.contains(applicationClass)) {
- licensePrintedForApplicationClass.add(applicationClass);
- if (license.shouldLimitsBePrintedOnInit())
- System.out.print(license.getDescription());
- }
-
- // Check license validity
- try {
- license.check(applicationClass, getNumberOfActiveUsers() + 1,
- VERSION_MAJOR, VERSION_MINOR, "IT Mill Toolkit", null);
- } catch (LicenseFileHasNotBeenRead e) {
- application.close();
- throw e;
- } catch (LicenseSignatureIsInvalid e) {
- application.close();
- throw e;
- } catch (InvalidLicenseFile e) {
- application.close();
- throw e;
- } catch (LicenseViolation e) {
- application.close();
- throw e;
- }
- }
-
- /**
- * Get the number of active application-user pairs.
- *
- * This returns total number of all applications in the server that are
- * considered to be active. For an application to be active, it must have
- * been accessed less than ACTIVE_USER_REQUEST_INTERVAL ms.
- *
- * @return Number of active application instances in the server.
- */
- private int getNumberOfActiveUsers() {
-
- Set apps = applicationToLastRequestDate.keySet();
- int active = 0;
- long now = System.currentTimeMillis();
- for (Iterator i = apps.iterator(); i.hasNext();) {
- Date lastReq = (Date) applicationToLastRequestDate.get(i.next());
- if (now - lastReq.getTime() < ACTIVE_USER_REQUEST_INTERVAL)
- active++;
- }
-
- return active;
- }
-
- /** End application */
- private void endApplication(HttpServletRequest request,
- HttpServletResponse response, Application application)
- throws IOException {
-
- String logoutUrl = application.getLogoutURL();
- if (logoutUrl == null)
- logoutUrl = application.getURL().toString();
-
- HttpSession session = request.getSession();
- if (session != null) {
- LinkedList applications = (LinkedList) session
- .getAttribute(SESSION_ATTR_APPS);
- if (applications != null)
- applications.remove(application);
- }
-
- response.sendRedirect(response.encodeRedirectURL(logoutUrl));
- }
-
- /**
- * Get the existing application or create a new one. Get a window within an
- * application based on the requested URI.
- *
- * @param request
- * HTTP Request.
- * @param application
- * Application to query for window.
- * @return Window mathing the given URI or null if not found.
- */
- private Window getApplicationWindow(HttpServletRequest request,
- Application application, Map params) throws ServletException {
-
- Window window = null;
-
- // Find the window where the request is handled
- String path = request.getPathInfo();
-
- // Main window as the URI is empty
- if (path == null || path.length() == 0 || path.equals("/"))
- window = application.getMainWindow();
-
- // Try to search by window name
- else {
- String windowName = null;
- if (path.charAt(0) == '/')
- path = path.substring(1);
- 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) {
-
- // If the window has existed, and is now removed
- // send a blank page
- if (allWindows.contains(windowName))
- return 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;
- }
- }
-
- // Create and open new debug window for application if requested
- Window debugWindow = application.getWindow(DebugWindow.WINDOW_NAME);
- if (debugWindow == null) {
- if (isDebugMode(params))
- try {
- debugWindow = new DebugWindow(application, request
- .getSession(false), this);
- debugWindow.setWidth(370);
- debugWindow.setHeight(480);
- application.addWindow(debugWindow);
- } catch (Exception e) {
- throw new ServletException(
- "Failed to create debug window for application", e);
- }
- } else if (window != debugWindow) {
- if (isDebugMode(params))
- debugWindow.requestRepaint();
- else
- application.removeWindow(debugWindow);
- }
-
- return window;
- }
-
- /**
- * Get relative location of a theme resource.
- *
- * @param theme
- * Theme name
- * @param resource
- * 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();
- }
-
- /**
- * Check if web adapter is in debug mode. Extra output is generated to log
- * when debug mode is enabled.
- *
- * @return Debug mode
- */
- public boolean isDebugMode(Map parameters) {
- if (parameters != null) {
- Object[] debug = (Object[]) parameters.get("debug");
- if (debug != null && !"false".equals(debug[0].toString())
- && !"false".equals(debugMode))
- return true;
- }
- return "true".equals(debugMode);
- }
-
- /**
- * Returns the theme source.
- *
- * @return ThemeSource
- */
- public ThemeSource getThemeSource() {
- return themeSource;
- }
-
- protected void addDirtyWindow(Application application, Window window) {
- synchronized (applicationToDirtyWindowSetMap) {
- HashSet dirtyWindows = (HashSet) applicationToDirtyWindowSetMap
- .get(application);
- if (dirtyWindows == null) {
- dirtyWindows = new HashSet();
- applicationToDirtyWindowSetMap.put(application, dirtyWindows);
- }
- dirtyWindows.add(window);
- }
- }
-
- protected void removeDirtyWindow(Application application, Window window) {
- synchronized (applicationToDirtyWindowSetMap) {
- HashSet dirtyWindows = (HashSet) applicationToDirtyWindowSetMap
- .get(application);
- if (dirtyWindows != null)
- dirtyWindows.remove(window);
- }
- }
-
- /**
- * @see com.itmill.toolkit.Application.WindowAttachListener#windowAttached(Application.WindowAttachEvent)
- */
- public void windowAttached(WindowAttachEvent event) {
- Window win = event.getWindow();
- win.addListener((Paintable.RepaintRequestListener) this);
-
- // Add to window names
- allWindows.add(win.getName());
-
- // Add window to dirty window references if it is visible
- // Or request the window to pass on the repaint requests
- if (win.isVisible())
- addDirtyWindow(event.getApplication(), win);
- else
- win.requestRepaintRequests();
-
- }
-
- /**
- * @see com.itmill.toolkit.Application.WindowDetachListener#windowDetached(Application.WindowDetachEvent)
- */
- public void windowDetached(WindowDetachEvent event) {
- event.getWindow().removeListener(
- (Paintable.RepaintRequestListener) this);
-
- // Add dirty window reference for closing the window
- addDirtyWindow(event.getApplication(), event.getWindow());
- }
-
- /**
- * @see com.itmill.toolkit.terminal.Paintable.RepaintRequestListener#repaintRequested(Paintable.RepaintRequestEvent)
- */
- public void repaintRequested(RepaintRequestEvent event) {
-
- Paintable p = event.getPaintable();
- Application app = null;
- if (p instanceof Window)
- app = ((Window) p).getApplication();
-
- if (app != null)
- addDirtyWindow(app, ((Window) p));
-
- Object lock = applicationToServerCommandStreamLock.get(app);
- if (lock != null)
- synchronized (lock) {
- lock.notifyAll();
- }
- }
-
- /** Get the list of dirty windows in application */
- protected Set getDirtyWindows(Application app) {
- HashSet dirtyWindows;
- synchronized (applicationToDirtyWindowSetMap) {
- dirtyWindows = (HashSet) applicationToDirtyWindowSetMap.get(app);
- }
- return dirtyWindows;
- }
-
- /** Remove a window from the list of dirty windows */
- private void windowPainted(Application app, Window window) {
- removeDirtyWindow(app, window);
- }
-
- /**
- * Generate server commands stream. If the server commands are not
- * requested, return false
- */
- private boolean handleServerCommands(HttpServletRequest request,
- HttpServletResponse response) {
-
- // Server commands are allways requested with certain parameter
- if (request.getParameter(SERVER_COMMAND_PARAM) == null)
- return false;
-
- // Get the application
- Application application;
- try {
- application = getApplication(request);
- } catch (MalformedURLException e) {
- return false;
- }
- if (application == null)
- return false;
-
- // Create continuous server commands stream
- try {
-
- // Writer for writing the stream
- PrintWriter w = new PrintWriter(response.getOutputStream());
-
- // Print necessary http page headers and padding
- w.println("<html><head></head><body>");
- for (int i = 0; i < SERVER_COMMAND_HEADER_PADDING; i++)
- w.print(' ');
-
- // Clock for synchronizing the stream
- Object lock = new Object();
- synchronized (applicationToServerCommandStreamLock) {
- Object oldlock = applicationToServerCommandStreamLock
- .get(application);
- if (oldlock != null)
- synchronized (oldlock) {
- oldlock.notifyAll();
- }
- applicationToServerCommandStreamLock.put(application, lock);
- }
- while (applicationToServerCommandStreamLock.get(application) == lock
- && application.isRunning()) {
- synchronized (application) {
-
- // Session expiration
- Date lastRequest = (Date) applicationToLastRequestDate
- .get(application);
- if (lastRequest != null
- && lastRequest.getTime()
- + request.getSession()
- .getMaxInactiveInterval() * 1000 < System
- .currentTimeMillis()) {
-
- // Session expired, close application
- application.close();
- } else {
-
- // Application still alive - keep updating windows
- Set dws = getDirtyWindows(application);
- if (dws != null && !dws.isEmpty()) {
-
- // For one of the dirty windows (in each
- // application)
- // request redraw
- Window win = (Window) dws.iterator().next();
- w
- .println("<script>\n"
- + ThemeFunctionLibrary
- .getWindowRefreshScript(
- application,
- win,
- WebBrowserProbe
- .getTerminalType(request
- .getSession()))
- + "</script>");
-
- removeDirtyWindow(application, win);
-
- // Windows that are closed immediately are "painted"
- // now
- if (win.getApplication() == null
- || !win.isVisible())
- win.requestRepaintRequests();
- }
- }
- }
-
- // Send the generated commands and newline immediately to
- // browser
- w.println(" ");
- w.flush();
- response.flushBuffer();
-
- synchronized (lock) {
- try {
- lock.wait(SERVER_COMMAND_STREAM_MAINTAIN_PERIOD);
- } catch (InterruptedException ignored) {
- }
- }
- }
- } catch (IOException ignore) {
-
- // In case of an Exceptions the server command stream is
- // terminated
- synchronized (applicationToServerCommandStreamLock) {
- if (applicationToServerCommandStreamLock.get(application) == application)
- applicationToServerCommandStreamLock.remove(application);
- }
- }
-
- return true;
- }
-
- private class SessionBindingListener implements HttpSessionBindingListener {
- private LinkedList applications;
-
- protected SessionBindingListener(LinkedList applications) {
- this.applications = applications;
- }
-
- /**
- * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
- */
- public void valueBound(HttpSessionBindingEvent arg0) {
- // We are not interested in bindings
- }
-
- /**
- * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
- */
- public void valueUnbound(HttpSessionBindingEvent event) {
-
- // If the binding listener is unbound from the session, the
- // session must be closing
- if (event.getName().equals(SESSION_BINDING_LISTENER)) {
-
- // Close all applications
- Object[] apps = applications.toArray();
- for (int i = 0; i < apps.length; i++) {
- if (apps[i] != null) {
-
- // Close app
- ((Application) apps[i]).close();
-
- // Stop application server commands stream
- Object lock = applicationToServerCommandStreamLock
- .get(apps[i]);
- if (lock != null)
- synchronized (lock) {
- lock.notifyAll();
- }
- applicationToServerCommandStreamLock.remove(apps[i]);
-
- // Remove application from applications list
- applications.remove(apps[i]);
- }
- }
- }
- }
-
- }
-
- /** Implementation of ParameterHandler.ErrorEvent interface. */
- public class ParameterHandlerErrorImpl implements
- ParameterHandler.ErrorEvent {
-
- private ParameterHandler owner;
-
- private Throwable throwable;
-
- private ParameterHandlerErrorImpl(ParameterHandler owner,
- Throwable throwable) {
- this.owner = owner;
- this.throwable = throwable;
- }
-
- /**
- * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable()
- */
- public Throwable getThrowable() {
- return this.throwable;
- }
-
- /**
- * @see com.itmill.toolkit.terminal.ParameterHandler.ErrorEvent#getParameterHandler()
- */
- public ParameterHandler getParameterHandler() {
- return this.owner;
- }
-
- }
-
- /** Implementation of URIHandler.ErrorEvent interface. */
- public class URIHandlerErrorImpl implements URIHandler.ErrorEvent {
-
- private URIHandler owner;
-
- private Throwable throwable;
-
- private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) {
- this.owner = owner;
- this.throwable = throwable;
- }
-
- /**
- * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable()
- */
- public Throwable getThrowable() {
- return this.throwable;
- }
-
- /**
- * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler()
- */
- public URIHandler getURIHandler() {
- return this.owner;
- }
- }
-
- /**
- * Get AJAX application manager for an application.
- *
- * If this application has not been running in ajax mode before, new manager
- * is created and web adapter stops listening to changes.
- *
- * @param application
- * @return AJAX Application Manager
- */
- private AjaxApplicationManager getApplicationManager(Application application) {
- AjaxApplicationManager mgr = (AjaxApplicationManager) applicationToAjaxAppMgrMap
- .get(application);
-
- // This application is going from Web to AJAX mode, create new manager
- if (mgr == null) {
-
- // Create new manager
- mgr = new AjaxApplicationManager(application);
- applicationToAjaxAppMgrMap.put(application, mgr);
-
- // Stop sending changes to this servlet because manager will take
- // control
- application.removeListener((Application.WindowAttachListener) this);
- application.removeListener((Application.WindowDetachListener) this);
-
- // Deregister all window listeners
- for (Iterator wins = application.getWindows().iterator(); wins.hasNext();)
- ((Window)wins.next()).removeListener((Paintable.RepaintRequestListener)this);
-
- // Manager takes control over the application
- mgr.takeControl();
- }
- return mgr;
- }
- }
|