Source Code Cross Referenced for WebAdapterServlet.java in  » Web-Framework » Millstone » org » millstone » webadapter » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Framework » Millstone » org.millstone.webadapter 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* *************************************************************************
0002:         
0003:         Millstone(TM) 
0004:         Open Sourced User Interface Library for
0005:         Internet Development with Java
0006:
0007:         Millstone is a registered trademark of IT Mill Ltd
0008:         Copyright (C) 2000-2005 IT Mill Ltd
0009:         
0010:         *************************************************************************
0011:
0012:         This library is free software; you can redistribute it and/or
0013:         modify it under the terms of the GNU Lesser General Public
0014:         license version 2.1 as published by the Free Software Foundation.
0015:
0016:         This library is distributed in the hope that it will be useful,
0017:         but WITHOUT ANY WARRANTY; without even the implied warranty of
0018:         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0019:         Lesser General Public License for more details.
0020:
0021:         You should have received a copy of the GNU Lesser General Public
0022:         License along with this library; if not, write to the Free Software
0023:         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0024:
0025:         *************************************************************************
0026:         
0027:         For more information, contact:
0028:         
0029:         IT Mill Ltd                           phone: +358 2 4802 7180
0030:         Ruukinkatu 2-4                        fax:  +358 2 4802 7181
0031:         20540, Turku                          email: info@itmill.com
0032:         Finland                               company www: www.itmill.com
0033:         
0034:         Primary source for MillStone information and releases: www.millstone.org
0035:
0036:         ********************************************************************** */
0037:
0038:        package org.millstone.webadapter;
0039:
0040:        import java.io.BufferedReader;
0041:        import java.io.BufferedWriter;
0042:        import java.io.File;
0043:        import java.io.FileOutputStream;
0044:        import java.io.IOException;
0045:        import java.io.InputStream;
0046:        import java.io.InputStreamReader;
0047:        import java.io.OutputStream;
0048:        import java.io.OutputStreamWriter;
0049:        import java.io.PrintWriter;
0050:        import java.net.MalformedURLException;
0051:        import java.net.URL;
0052:        import java.util.Arrays;
0053:        import java.util.Collection;
0054:        import java.util.Date;
0055:        import java.util.Enumeration;
0056:        import java.util.HashSet;
0057:        import java.util.Iterator;
0058:        import java.util.LinkedList;
0059:        import java.util.List;
0060:        import java.util.Map;
0061:        import java.util.Properties;
0062:        import java.util.Set;
0063:        import java.util.StringTokenizer;
0064:        import java.util.WeakHashMap;
0065:
0066:        import javax.servlet.ServletContext;
0067:        import javax.servlet.ServletException;
0068:        import javax.servlet.http.HttpServlet;
0069:        import javax.servlet.http.HttpServletRequest;
0070:        import javax.servlet.http.HttpServletResponse;
0071:        import javax.servlet.http.HttpSession;
0072:        import javax.servlet.http.HttpSessionBindingEvent;
0073:        import javax.servlet.http.HttpSessionBindingListener;
0074:
0075:        import org.millstone.base.Application;
0076:        import org.millstone.base.Application.WindowAttachEvent;
0077:        import org.millstone.base.Application.WindowDetachEvent;
0078:        import org.millstone.base.service.FileTypeResolver;
0079:        import org.millstone.base.terminal.DownloadStream;
0080:        import org.millstone.base.terminal.Paintable;
0081:        import org.millstone.base.terminal.ParameterHandler;
0082:        import org.millstone.base.terminal.ThemeResource;
0083:        import org.millstone.base.terminal.URIHandler;
0084:        import org.millstone.base.terminal.Paintable.RepaintRequestEvent;
0085:        import org.millstone.base.ui.Window;
0086:        import org.millstone.webadapter.ThemeSource.ThemeException;
0087:
0088:        /**
0089:         * This servlet is the core of the MillStone Web Adapter, that adapts the
0090:         * MillStone applications to Web standards. The web adapter can be used to
0091:         * represent the most MillStone application using Web browsers and corresponding
0092:         * technologies.
0093:         * 
0094:         * @author IT Mill Ltd.
0095:         * @version
0096:         * 3.1.1
0097:         * @since 3.0
0098:         */
0099:
0100:        public class WebAdapterServlet extends HttpServlet implements 
0101:                Application.WindowAttachListener,
0102:                Application.WindowDetachListener,
0103:                Paintable.RepaintRequestListener {
0104:
0105:            // Versions
0106:            private static final int VERSION_MAJOR = 3;
0107:            private static final int VERSION_MINOR = 1;
0108:            private static final int VERSION_BUILD = 0;
0109:            private static final String VERSION = "" + VERSION_MAJOR + "."
0110:                    + VERSION_MINOR + "." + VERSION_BUILD;
0111:
0112:            //Configurable parameter names
0113:            private static final String PARAMETER_DEBUG = "Debug";
0114:            private static final String PARAMETER_DEFAULT_THEME_JAR = "DefaultThemeJar";
0115:            private static final String PARAMETER_THEMESOURCE = "ThemeSource";
0116:            private static final String PARAMETER_THEME_CACHETIME = "ThemeCacheTime";
0117:            private static final String PARAMETER_MAX_TRANSFORMERS = "MaxTransformers";
0118:            private static final String PARAMETER_TRANSFORMER_CACHETIME = "TransformerCacheTime";
0119:
0120:            private static int DEFAULT_THEME_CACHETIME = 1000 * 60 * 60 * 24;
0121:            private static int DEFAULT_BUFFER_SIZE = 32 * 1024;
0122:            private static int DEFAULT_MAX_TRANSFORMERS = 1;
0123:            private static int MAX_BUFFER_SIZE = 64 * 1024;
0124:            private static String SESSION_ATTR_VARMAP = "varmap";
0125:            static String SESSION_ATTR_CONTEXT = "millstone_context";
0126:            static String SESSION_ATTR_APPS = "apps";
0127:            private static String SESSION_BINDING_LISTENER = "bindinglistener";
0128:            private static String SESSION_DEFAULT_THEME = "default";
0129:            private static String RESOURCE_URI = "/RES/";
0130:            private static String THEME_DIRECTORY_PATH = "WEB-INF/lib/themes/";
0131:            private static String THEME_LISTING_FILE = THEME_DIRECTORY_PATH
0132:                    + "themes.txt";
0133:            private static String DEFAULT_THEME_JAR_PREFIX = "millstone-web-themes";
0134:            private static String DEFAULT_THEME_JAR = "WEB-INF/lib/"
0135:                    + DEFAULT_THEME_JAR_PREFIX + "-" + VERSION + ".jar";
0136:            private static String DEFAULT_THEME_SNAPSHOT_JAR = "WEB-INF/lib/"
0137:                    + DEFAULT_THEME_JAR_PREFIX + "-" + VERSION_MAJOR + "."
0138:                    + VERSION_MINOR + "-SNAPSHOT.jar";
0139:            private static String DEFAULT_THEME_TEMP_FILE_PREFIX = "WA_TMP_";
0140:            private static String SERVER_COMMAND_PARAM = "SERVER_COMMANDS";
0141:            private static int SERVER_COMMAND_STREAM_MAINTAIN_PERIOD = 15000;
0142:            private static int SERVER_COMMAND_HEADER_PADDING = 2000;
0143:
0144:            // Private fields
0145:            private Class applicationClass;
0146:            private Properties applicationProperties;
0147:            private UIDLTransformerFactory transformerFactory;
0148:            private CollectionThemeSource themeSource;
0149:            private String resourcePath = null;
0150:            //private boolean enableBrowserProbe = false;
0151:            private boolean debugMode = false;
0152:            private int maxConcurrentTransformers;
0153:            private long transformerCacheTime;
0154:            private long themeCacheTime;
0155:            private WeakHashMap applicationToDirtyWindowSetMap = new WeakHashMap();
0156:            private WeakHashMap applicationToServerCommandStreamLock = new WeakHashMap();
0157:            private WeakHashMap applicationToLastRequestDate = new WeakHashMap();
0158:            private List allWindows = new LinkedList();
0159:
0160:            /**
0161:             * Called by the servlet container to indicate to a servlet that the servlet
0162:             * is being placed into service.
0163:             * 
0164:             * @param servletConfig
0165:             *            object containing the servlet's configuration and
0166:             *            initialization parameters
0167:             * @throws ServletException
0168:             *             if an exception has occurred that interferes with the
0169:             *             servlet's normal operation.
0170:             */
0171:            public void init(javax.servlet.ServletConfig servletConfig)
0172:                    throws javax.servlet.ServletException {
0173:                super .init(servletConfig);
0174:
0175:                // Get the application class name
0176:                String applicationClassName = servletConfig
0177:                        .getInitParameter("application");
0178:                if (applicationClassName == null) {
0179:                    Log
0180:                            .error("Application not specified in servlet parameters");
0181:                }
0182:
0183:                // Store the application parameters into Properties object
0184:                this .applicationProperties = new Properties();
0185:                for (Enumeration e = servletConfig.getInitParameterNames(); e
0186:                        .hasMoreElements();) {
0187:                    String name = (String) e.nextElement();
0188:                    this .applicationProperties.setProperty(name, servletConfig
0189:                            .getInitParameter(name));
0190:                }
0191:
0192:                // Override with server.xml parameters
0193:                ServletContext context = servletConfig.getServletContext();
0194:                for (Enumeration e = context.getInitParameterNames(); e
0195:                        .hasMoreElements();) {
0196:                    String name = (String) e.nextElement();
0197:                    this .applicationProperties.setProperty(name, context
0198:                            .getInitParameter(name));
0199:                }
0200:
0201:                // Get the debug window parameter
0202:                String debug = getApplicationOrSystemProperty(PARAMETER_DEBUG,
0203:                        "false");
0204:                // Enable application specific debug
0205:                this .debugMode = debug.equals("true");
0206:
0207:                // Get the maximum number of simultaneous transformers
0208:                this .maxConcurrentTransformers = Integer
0209:                        .parseInt(getApplicationOrSystemProperty(
0210:                                PARAMETER_MAX_TRANSFORMERS, "-1"));
0211:                if (this .maxConcurrentTransformers < 1)
0212:                    this .maxConcurrentTransformers = DEFAULT_MAX_TRANSFORMERS;
0213:
0214:                // Get cache time for transformers
0215:                this .transformerCacheTime = Integer
0216:                        .parseInt(getApplicationOrSystemProperty(
0217:                                PARAMETER_TRANSFORMER_CACHETIME, "-1")) * 1000;
0218:
0219:                // Get cache time for theme resources
0220:                this .themeCacheTime = Integer
0221:                        .parseInt(getApplicationOrSystemProperty(
0222:                                PARAMETER_THEME_CACHETIME, "-1")) * 1000;
0223:                if (this .themeCacheTime < 0) {
0224:                    this .themeCacheTime = DEFAULT_THEME_CACHETIME;
0225:                }
0226:
0227:                // Add all specified theme sources
0228:                this .themeSource = new CollectionThemeSource();
0229:                List directorySources = getThemeSources();
0230:                for (Iterator i = directorySources.iterator(); i.hasNext();) {
0231:                    this .themeSource.add((ThemeSource) i.next());
0232:                }
0233:
0234:                // Add the default theme source
0235:                String[] defaultThemeFiles = new String[] {
0236:                        getApplicationOrSystemProperty(
0237:                                PARAMETER_DEFAULT_THEME_JAR, DEFAULT_THEME_JAR),
0238:                        DEFAULT_THEME_SNAPSHOT_JAR
0239:
0240:                };
0241:                File f = findDefaultThemeJar(defaultThemeFiles);
0242:                try {
0243:                    // Add themes.jar if exists
0244:                    if (f != null && f.exists())
0245:                        this .themeSource.add(new JarThemeSource(f, this , ""));
0246:                    else {
0247:                        Log.warn("Default theme JAR not found in: "
0248:                                + Arrays.asList(defaultThemeFiles));
0249:                    }
0250:
0251:                } catch (Exception e) {
0252:                    throw new ServletException(
0253:                            "Failed to load default theme from "
0254:                                    + Arrays.asList(defaultThemeFiles), e);
0255:                }
0256:
0257:                // Check that at least one themesource was loaded
0258:                if (this .themeSource.getThemes().size() <= 0) {
0259:                    throw new ServletException(
0260:                            "No themes found in specified themesources.");
0261:                }
0262:
0263:                // Initialize the transformer factory, if not initialized
0264:                if (this .transformerFactory == null) {
0265:
0266:                    this .transformerFactory = new UIDLTransformerFactory(
0267:                            this .themeSource, this ,
0268:                            this .maxConcurrentTransformers,
0269:                            this .transformerCacheTime);
0270:                }
0271:
0272:                // Load the application class using the same class loader
0273:                // as the servlet itself
0274:                ClassLoader loader = this .getClass().getClassLoader();
0275:                try {
0276:                    this .applicationClass = loader
0277:                            .loadClass(applicationClassName);
0278:                } catch (ClassNotFoundException e) {
0279:                    throw new ServletException(
0280:                            "Failed to load application class: "
0281:                                    + applicationClassName);
0282:                }
0283:            }
0284:
0285:            /**
0286:             * Get an application or system property value.
0287:             * 
0288:             * @param parameterName
0289:             *            Name or the parameter
0290:             * @param defaultValue
0291:             *            Default to be used
0292:             * @return String value or default if not found
0293:             */
0294:            private String getApplicationOrSystemProperty(String parameterName,
0295:                    String defaultValue) {
0296:
0297:                // Try application properties
0298:                String val = this .applicationProperties
0299:                        .getProperty(parameterName);
0300:                if (val != null) {
0301:                    return val;
0302:                }
0303:
0304:                // Try lowercased application properties for backward compability with
0305:                // 3.0.2 and earlier
0306:                val = this .applicationProperties.getProperty(parameterName
0307:                        .toLowerCase());
0308:                if (val != null) {
0309:                    return val;
0310:                }
0311:
0312:                // Try system properties
0313:                String pkgName;
0314:                Package pkg = this .getClass().getPackage();
0315:                if (pkg != null) {
0316:                    pkgName = pkg.getName();
0317:                } else {
0318:                    String clazzName = this .getClass().getName();
0319:                    pkgName = new String(clazzName.toCharArray(), 0, clazzName
0320:                            .lastIndexOf('.'));
0321:                }
0322:                val = System.getProperty(pkgName + "." + parameterName);
0323:                if (val != null) {
0324:                    return val;
0325:                }
0326:
0327:                // Try lowercased system properties
0328:                val = System.getProperty(pkgName + "."
0329:                        + parameterName.toLowerCase());
0330:                if (val != null) {
0331:                    return val;
0332:                }
0333:
0334:                return defaultValue;
0335:            }
0336:
0337:            /**
0338:             * Get ThemeSources from given path. Construct the list of avalable themes
0339:             * in path using the following sources: 1. content of THEME_PATH directory
0340:             * (if available) 2. The themes listed in THEME_LIST_FILE 3. "themesource"
0341:             * application parameter - "org. millstone.webadapter. themesource" system
0342:             * property
0343:             * 
0344:             * @param THEME_DIRECTORY_PATH
0345:             * @return List
0346:             */
0347:            private List getThemeSources() throws ServletException {
0348:
0349:                List returnValue = new LinkedList();
0350:
0351:                // Check the list file in theme directory
0352:                List sourcePaths = new LinkedList();
0353:                try {
0354:                    BufferedReader reader = new BufferedReader(
0355:                            new InputStreamReader(this .getServletContext()
0356:                                    .getResourceAsStream(THEME_LISTING_FILE)));
0357:                    String line = null;
0358:                    while ((line = reader.readLine()) != null) {
0359:                        sourcePaths.add(THEME_DIRECTORY_PATH + line.trim());
0360:                    }
0361:                    if (this .isDebugMode()) {
0362:                        Log.debug("Listed " + sourcePaths.size()
0363:                                + " themes in " + THEME_LISTING_FILE
0364:                                + ". Loading " + sourcePaths);
0365:                    }
0366:                } catch (Exception ignored) {
0367:                    // If the file reading fails, just skip to next method
0368:                }
0369:
0370:                // If no file was found or it was empty,
0371:                // try to add themes filesystem directory if it is accessible
0372:                if (sourcePaths.size() <= 0) {
0373:                    if (this .isDebugMode()) {
0374:                        Log.debug("No themes listed in " + THEME_LISTING_FILE
0375:                                + ". Trying to read the content of directory "
0376:                                + THEME_DIRECTORY_PATH);
0377:                    }
0378:
0379:                    try {
0380:                        String path = this .getServletContext().getRealPath(
0381:                                THEME_DIRECTORY_PATH);
0382:                        if (path != null) {
0383:                            File f = new File(path);
0384:                            if (f != null && f.exists())
0385:                                returnValue.add(new DirectoryThemeSource(f,
0386:                                        this ));
0387:                        }
0388:                    } catch (java.io.IOException je) {
0389:                        Log.info("Theme directory " + THEME_DIRECTORY_PATH
0390:                                + " not available. Skipped.");
0391:                    } catch (ThemeException e) {
0392:                        throw new ServletException(
0393:                                "Failed to load themes from "
0394:                                        + THEME_DIRECTORY_PATH, e);
0395:                    }
0396:                }
0397:
0398:                // Add the theme sources from application properties
0399:                String paramValue = getApplicationOrSystemProperty(
0400:                        PARAMETER_THEMESOURCE, null);
0401:                if (paramValue != null) {
0402:                    StringTokenizer st = new StringTokenizer(paramValue, ";");
0403:                    while (st.hasMoreTokens()) {
0404:                        sourcePaths.add(st.nextToken());
0405:                    }
0406:                }
0407:
0408:                // Construct appropriate theme source instances for each path
0409:                for (Iterator i = sourcePaths.iterator(); i.hasNext();) {
0410:                    String source = (String) i.next();
0411:                    File sourceFile = new File(source);
0412:                    try {
0413:
0414:                        // Relative files are treated as streams (to support
0415:                        // resource inside WAR files)
0416:                        if (!sourceFile.isAbsolute()) {
0417:                            returnValue.add(new ServletThemeSource(this 
0418:                                    .getServletContext(), this , source));
0419:                        } else if (sourceFile.isDirectory()) {
0420:
0421:                            // Absolute directories are read from filesystem
0422:                            returnValue.add(new DirectoryThemeSource(
0423:                                    sourceFile, this ));
0424:                        } else {
0425:
0426:                            // Absolute JAR-files are read from filesystem
0427:                            returnValue.add(new JarThemeSource(sourceFile,
0428:                                    this , ""));
0429:                        }
0430:                    } catch (Exception e) {
0431:                        // Any exception breaks the the init
0432:                        throw new ServletException("Invalid theme source: "
0433:                                + source, e);
0434:                    }
0435:                }
0436:
0437:                // Return the constructed list of theme sources
0438:                return returnValue;
0439:            }
0440:
0441:            /**
0442:             * Receives standard HTTP requests from the public service method and
0443:             * dispatches them.
0444:             * 
0445:             * @param request
0446:             *            object that contains the request the client made of the
0447:             *            servlet
0448:             * @param response
0449:             *            object that contains the response the servlet returns to the
0450:             *            client
0451:             * @throws ServletException
0452:             *             if an input or output error occurs while the servlet is
0453:             *             handling the TRACE request
0454:             * @throws IOException
0455:             *             if the request for the TRACE cannot be handled
0456:             */
0457:            protected void service(HttpServletRequest request,
0458:                    HttpServletResponse response) throws ServletException,
0459:                    IOException {
0460:
0461:                // Transformer and output stream for the result
0462:                UIDLTransformer transformer = null;
0463:                HttpVariableMap variableMap = null;
0464:                OutputStream out = response.getOutputStream();
0465:                HashSet currentlyDirtyWindowsForThisApplication = new HashSet();
0466:                try {
0467:
0468:                    // If the resource path is unassigned, initialize it
0469:                    if (resourcePath == null)
0470:                        resourcePath = request.getContextPath()
0471:                                + request.getServletPath() + RESOURCE_URI;
0472:
0473:                    // Handle resource requests
0474:                    if (handleResourceRequest(request, response))
0475:                        return;
0476:
0477:                    // Handle server commands
0478:                    if (handleServerCommands(request, response))
0479:                        return;
0480:
0481:                    // Get the application
0482:                    Application application = getApplication(request);
0483:
0484:                    // Create application if it doesn't exist
0485:                    if (application == null)
0486:                        application = createApplication(request);
0487:
0488:                    // Is this a download request from application
0489:                    DownloadStream download = null;
0490:
0491:                    // Invoke context transaction listeners
0492:                    WebApplicationContext appContext = null;
0493:                    if (application != null) {
0494:                        appContext = (WebApplicationContext) application
0495:                                .getContext();
0496:                    }
0497:                    if (appContext != null) {
0498:                        appContext.startTransaction(application, request);
0499:                    }
0500:
0501:                    // The rest of the process is synchronized with the application
0502:                    // in order to guarantee that no parallel variable handling is
0503:                    // made
0504:                    synchronized (application) {
0505:
0506:                        // Set the last application request date
0507:                        applicationToLastRequestDate.put(application,
0508:                                new Date());
0509:
0510:                        // Get the variable map
0511:                        variableMap = getVariableMap(application, request);
0512:                        if (variableMap == null)
0513:                            return;
0514:
0515:                        // Change all variables based on request parameters
0516:                        Map unhandledParameters = variableMap.handleVariables(
0517:                                request, application);
0518:
0519:                        // Check/handle client side feature checks
0520:                        WebBrowserProbe.handleProbeRequest(request,
0521:                                unhandledParameters);
0522:
0523:                        // Handle the URI if the application is still running
0524:                        if (application.isRunning())
0525:                            download = handleURI(application, request, response);
0526:
0527:                        // If this is not a download request
0528:                        if (download == null) {
0529:
0530:                            // Window renders are not cacheable
0531:                            response.setHeader("Cache-Control", "no-cache");
0532:                            response.setHeader("Pragma", "no-cache");
0533:                            response.setDateHeader("Expires", 0);
0534:
0535:                            // Find the window within the application
0536:                            Window window = null;
0537:                            if (application.isRunning())
0538:                                window = getApplicationWindow(request,
0539:                                        application);
0540:
0541:                            // Handle the unhandled parameters if the application is
0542:                            // still running
0543:                            if (window != null && unhandledParameters != null
0544:                                    && !unhandledParameters.isEmpty()) {
0545:                                try {
0546:                                    window
0547:                                            .handleParameters(unhandledParameters);
0548:                                } catch (Throwable t) {
0549:                                    application
0550:                                            .terminalError(new ParameterHandlerErrorImpl(
0551:                                                    window, t));
0552:                                }
0553:                            }
0554:                            // Remove application if it has stopped
0555:                            if (!application.isRunning()) {
0556:                                endApplication(request, response, application);
0557:                                return;
0558:                            }
0559:
0560:                            // Return blank page, if no window found
0561:                            if (window == null) {
0562:                                response.setContentType("text/html");
0563:                                BufferedWriter page = new BufferedWriter(
0564:                                        new OutputStreamWriter(out));
0565:                                page.write("<html><head><script>");
0566:                                page
0567:                                        .write(ThemeFunctionLibrary
0568:                                                .generateWindowScript(
0569:                                                        null,
0570:                                                        application,
0571:                                                        this ,
0572:                                                        WebBrowserProbe
0573:                                                                .getTerminalType(request
0574:                                                                        .getSession())));
0575:                                page.write("</script></head><body>");
0576:                                page
0577:                                        .write("The requested window has been removed from application.");
0578:                                page.write("</body></html>");
0579:                                page.close();
0580:
0581:                                return;
0582:                            }
0583:
0584:                            // Get the terminal type for the window
0585:                            WebBrowser terminalType = (WebBrowser) window
0586:                                    .getTerminal();
0587:
0588:                            // Set terminal type for the window, if not already set
0589:                            if (terminalType == null) {
0590:                                terminalType = WebBrowserProbe
0591:                                        .getTerminalType(request.getSession());
0592:                                window.setTerminal(terminalType);
0593:                            }
0594:
0595:                            // Find theme and initialize TransformerType
0596:                            UIDLTransformerType transformerType = null;
0597:                            if (window.getTheme() != null) {
0598:                                Theme activeTheme;
0599:                                if ((activeTheme = this .themeSource
0600:                                        .getThemeByName(window.getTheme())) != null) {
0601:                                    transformerType = new UIDLTransformerType(
0602:                                            terminalType, activeTheme);
0603:                                } else {
0604:                                    Log
0605:                                            .info("Theme named '"
0606:                                                    + window.getTheme()
0607:                                                    + "' not found. Using system default theme.");
0608:                                }
0609:                            }
0610:
0611:                            // Use default theme if selected theme was not found.
0612:                            if (transformerType == null) {
0613:                                Theme defaultTheme = this .themeSource
0614:                                        .getThemeByName(WebAdapterServlet.SESSION_DEFAULT_THEME);
0615:                                if (defaultTheme == null) {
0616:                                    throw new ServletException(
0617:                                            "Default theme not found in the specified theme source(s).");
0618:                                }
0619:                                transformerType = new UIDLTransformerType(
0620:                                        terminalType, defaultTheme);
0621:                            }
0622:
0623:                            transformer = this .transformerFactory
0624:                                    .getTransformer(transformerType);
0625:
0626:                            // Set the response type
0627:                            response.setContentType(terminalType
0628:                                    .getContentType());
0629:
0630:                            // Create UIDL writer
0631:                            WebPaintTarget paintTarget = transformer
0632:                                    .getPaintTarget(variableMap);
0633:
0634:                            // Assure that the correspoding debug window will be
0635:                            // repainted property
0636:                            // by clearing it before the actual paint.
0637:                            DebugWindow debugWindow = (DebugWindow) application
0638:                                    .getWindow(DebugWindow.WINDOW_NAME);
0639:                            if (debugWindow != null && debugWindow != window) {
0640:                                debugWindow
0641:                                        .setWindowUIDL(window, "Painting...");
0642:                            }
0643:
0644:                            // Paint window
0645:                            window.paint(paintTarget);
0646:                            paintTarget.close();
0647:
0648:                            // For exception handling, memorize the current dirty status
0649:                            Collection dirtyWindows = (Collection) applicationToDirtyWindowSetMap
0650:                                    .get(application);
0651:                            if (dirtyWindows == null) {
0652:                                dirtyWindows = new HashSet();
0653:                                applicationToDirtyWindowSetMap.put(application,
0654:                                        dirtyWindows);
0655:                            }
0656:                            currentlyDirtyWindowsForThisApplication
0657:                                    .addAll(dirtyWindows);
0658:
0659:                            // Window is now painted
0660:                            windowPainted(application, window);
0661:
0662:                            // Debug
0663:                            if (debugWindow != null && debugWindow != window) {
0664:                                debugWindow.setWindowUIDL(window, paintTarget
0665:                                        .getUIDL());
0666:                            }
0667:
0668:                            // Set the function library state for this thread
0669:                            ThemeFunctionLibrary.setState(application, window,
0670:                                    transformerType.getWebBrowser(), request
0671:                                            .getSession(), this ,
0672:                                    transformerType.getTheme().getName());
0673:
0674:                        }
0675:                    }
0676:
0677:                    // For normal requests, transform the window
0678:                    if (download == null) {
0679:
0680:                        // Transform and output the result to browser
0681:                        // Note that the transform and transfer of the result is
0682:                        // not synchronized with the variable map. This allows
0683:                        // parallel transfers and transforms for better performance,
0684:                        // but requires that all calls from the XSL to java are
0685:                        // thread-safe
0686:                        transformer.transform(out);
0687:                    }
0688:
0689:                    // For download request, transfer the downloaded data
0690:                    else {
0691:
0692:                        handleDownload(download, request, response);
0693:                    }
0694:
0695:                    // Notify context of transaction end
0696:                    if (appContext != null) {
0697:                        appContext.endTransaction(application, request);
0698:                    }
0699:
0700:                } catch (UIDLTransformerException te) {
0701:
0702:                    try {
0703:                        // Write the error report to client
0704:                        response.setContentType("text/html");
0705:                        BufferedWriter err = new BufferedWriter(
0706:                                new OutputStreamWriter(out));
0707:                        err
0708:                                .write("<html><head><title>Application Internal Error</title></head><body>");
0709:                        err.write("<h1>" + te.getMessage() + "</h1>");
0710:                        err.write(te.getHTMLDescription());
0711:                        err.write("</body></html>");
0712:                        err.close();
0713:                    } catch (Throwable t) {
0714:                        Log.except("Failed to write error page: " + t
0715:                                + ". Original exception was: ", te);
0716:                    }
0717:
0718:                    // Add previously dirty windows to dirtyWindowList in order
0719:                    // to make sure that eventually they are repainted
0720:                    Application currentApplication = getApplication(request);
0721:                    for (Iterator iter = currentlyDirtyWindowsForThisApplication
0722:                            .iterator(); iter.hasNext();) {
0723:                        Window dirtyWindow = (Window) iter.next();
0724:                        addDirtyWindow(currentApplication, dirtyWindow);
0725:                    }
0726:
0727:                } catch (Throwable e) {
0728:                    // Re-throw other exceptions
0729:                    throw new ServletException(e);
0730:                } finally {
0731:
0732:                    // Release transformer
0733:                    if (transformer != null)
0734:                        transformerFactory.releaseTransformer(transformer);
0735:
0736:                    // Clean the function library state for this thread
0737:                    // for security reasons
0738:                    ThemeFunctionLibrary.cleanState();
0739:                }
0740:            }
0741:
0742:            /**
0743:             * Handle the requested URI. An application can add handlers to do special
0744:             * processing, when a certain URI is requested. The handlers are invoked
0745:             * before any windows URIs are processed and if a DownloadStream is returned
0746:             * it is sent to the client.
0747:             * 
0748:             * @see org.millstone.base.terminal.URIHandler
0749:             * 
0750:             * @param application
0751:             *            Application owning the URI
0752:             * @param request
0753:             *            HTTP request instance
0754:             * @param response
0755:             *            HTTP response to write to.
0756:             * @return boolean True if the request was handled and further processing
0757:             *         should be suppressed, false otherwise.
0758:             */
0759:            private DownloadStream handleURI(Application application,
0760:                    HttpServletRequest request, HttpServletResponse response) {
0761:
0762:                String uri = request.getPathInfo();
0763:
0764:                // If no URI is available
0765:                if (uri == null || uri.length() == 0 || uri.equals("/"))
0766:                    return null;
0767:
0768:                // Remove the leading /
0769:                while (uri.startsWith("/") && uri.length() > 0)
0770:                    uri = uri.substring(1);
0771:
0772:                // Handle the uri
0773:                DownloadStream stream = null;
0774:                try {
0775:                    stream = application.handleURI(application.getURL(), uri);
0776:                } catch (Throwable t) {
0777:                    application.terminalError(new URIHandlerErrorImpl(
0778:                            application, t));
0779:                }
0780:
0781:                return stream;
0782:            }
0783:
0784:            /**
0785:             * Handle the requested URI. An application can add handlers to do special
0786:             * processing, when a certain URI is requested. The handlers are invoked
0787:             * before any windows URIs are processed and if a DownloadStream is returned
0788:             * it is sent to the client.
0789:             * 
0790:             * @see org.millstone.base.terminal.URIHandler
0791:             * 
0792:             * @param application
0793:             *            Application owning the URI
0794:             * @param request
0795:             *            HTTP request instance
0796:             * @param response
0797:             *            HTTP response to write to.
0798:             * @return boolean True if the request was handled and further processing
0799:             *         should be suppressed, false otherwise.
0800:             */
0801:            private void handleDownload(DownloadStream stream,
0802:                    HttpServletRequest request, HttpServletResponse response) {
0803:
0804:                // Download from given stream
0805:                InputStream data = stream.getStream();
0806:                if (data != null) {
0807:
0808:                    // Set content type
0809:                    response.setContentType(stream.getContentType());
0810:
0811:                    // Set cache headers
0812:                    long cacheTime = stream.getCacheTime();
0813:                    if (cacheTime <= 0) {
0814:                        response.setHeader("Cache-Control", "no-cache");
0815:                        response.setHeader("Pragma", "no-cache");
0816:                        response.setDateHeader("Expires", 0);
0817:                    } else {
0818:                        response.setHeader("Cache-Control", "max-age="
0819:                                + cacheTime / 1000);
0820:                        response.setDateHeader("Expires", System
0821:                                .currentTimeMillis()
0822:                                + cacheTime);
0823:                        response.setHeader("Pragma", "cache"); // Required to apply
0824:                        // caching in some
0825:                        // Tomcats
0826:                    }
0827:
0828:                    // Copy download stream parameters directly
0829:                    // to HTTP headers.
0830:                    Iterator i = stream.getParameterNames();
0831:                    if (i != null) {
0832:                        while (i.hasNext()) {
0833:                            String param = (String) i.next();
0834:                            response.setHeader((String) param, stream
0835:                                    .getParameter(param));
0836:                        }
0837:                    }
0838:
0839:                    int bufferSize = stream.getBufferSize();
0840:                    if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE)
0841:                        bufferSize = DEFAULT_BUFFER_SIZE;
0842:                    byte[] buffer = new byte[bufferSize];
0843:                    int bytesRead = 0;
0844:
0845:                    try {
0846:                        OutputStream out = response.getOutputStream();
0847:
0848:                        while ((bytesRead = data.read(buffer)) > 0) {
0849:                            out.write(buffer, 0, bytesRead);
0850:                            out.flush();
0851:                        }
0852:                        out.close();
0853:                    } catch (IOException ignored) {
0854:                    }
0855:
0856:                }
0857:
0858:            }
0859:
0860:            /**
0861:             * Look for default theme JAR file.
0862:             * 
0863:             * @return Jar file or null if not found.
0864:             */
0865:            private File findDefaultThemeJar(String[] fileList) {
0866:
0867:                // Try to find the default theme JAR file based on the given path
0868:                for (int i = 0; i < fileList.length; i++) {
0869:                    String path = this .getServletContext().getRealPath(
0870:                            fileList[i]);
0871:                    File file = null;
0872:                    if (path != null && (file = new File(path)).exists()) {
0873:                        return file;
0874:                    }
0875:                }
0876:
0877:                // If we do not have access to individual files, create a temporary
0878:                // file from named resource.
0879:                for (int i = 0; i < fileList.length; i++) {
0880:                    InputStream defaultTheme = this .getServletContext()
0881:                            .getResourceAsStream(fileList[i]);
0882:                    // Read the content to temporary file and return it
0883:                    if (defaultTheme != null) {
0884:                        return createTemporaryFile(defaultTheme, ".jar");
0885:                    }
0886:                }
0887:
0888:                // Try to find the default theme JAR file based on file naming scheme
0889:                // NOTE: This is for backward compability with 3.0.2 and earlier.
0890:                String path = this .getServletContext().getRealPath(
0891:                        "/WEB-INF/lib");
0892:                if (path != null) {
0893:
0894:                    File lib = new File(path);
0895:                    String[] files = lib.list();
0896:                    if (files != null) {
0897:                        for (int i = 0; i < files.length; i++) {
0898:                            if (files[i].toLowerCase().endsWith(".jar")
0899:                                    && files[i]
0900:                                            .startsWith(DEFAULT_THEME_JAR_PREFIX)) {
0901:                                return new File(lib, files[i]);
0902:                            }
0903:                        }
0904:                    }
0905:                }
0906:
0907:                // If no file was found return null
0908:                return null;
0909:            }
0910:
0911:            /**
0912:             * Create a temporary file for given stream.
0913:             * 
0914:             * @param stream
0915:             *            Stream to be stored into temporary file.
0916:             * @param extension
0917:             *            File type extension
0918:             * @return File
0919:             */
0920:            private File createTemporaryFile(InputStream stream,
0921:                    String extension) {
0922:                File tmpFile;
0923:                try {
0924:                    tmpFile = File.createTempFile(
0925:                            DEFAULT_THEME_TEMP_FILE_PREFIX, extension);
0926:                    FileOutputStream out = new FileOutputStream(tmpFile);
0927:                    byte[] buf = new byte[1024];
0928:                    int bytes = 0;
0929:                    while ((bytes = stream.read(buf)) > 0) {
0930:                        out.write(buf, 0, bytes);
0931:                    }
0932:                    out.close();
0933:                } catch (IOException e) {
0934:                    System.err
0935:                            .println("Failed to create temporary file for default theme: "
0936:                                    + e);
0937:                    tmpFile = null;
0938:                }
0939:
0940:                return tmpFile;
0941:            }
0942:
0943:            /**
0944:             * Handle theme resource file requests. Resources supplied with the themes
0945:             * are provided by the WebAdapterServlet.
0946:             * 
0947:             * @param request
0948:             *            HTTP request
0949:             * @param response
0950:             *            HTTP response
0951:             * @return boolean True if the request was handled and further processing
0952:             *         should be suppressed, false otherwise.
0953:             */
0954:            private boolean handleResourceRequest(HttpServletRequest request,
0955:                    HttpServletResponse response) throws ServletException {
0956:
0957:                String resourceId = request.getPathInfo();
0958:
0959:                // Check if this really is a resource request
0960:                if (resourceId == null || !resourceId.startsWith(RESOURCE_URI))
0961:                    return false;
0962:
0963:                // Check the resource type
0964:                resourceId = resourceId.substring(RESOURCE_URI.length());
0965:                InputStream data = null;
0966:                // Get theme resources
0967:                try {
0968:                    data = themeSource.getResource(resourceId);
0969:                } catch (ThemeSource.ThemeException e) {
0970:                    Log.info(e.getMessage());
0971:                    data = null;
0972:                }
0973:
0974:                // Write the response
0975:                try {
0976:                    if (data != null) {
0977:                        response.setContentType(FileTypeResolver
0978:                                .getMIMEType(resourceId));
0979:
0980:                        // Use default cache time for theme resources
0981:                        if (this .themeCacheTime > 0) {
0982:                            response.setHeader("Cache-Control", "max-age="
0983:                                    + this .themeCacheTime / 1000);
0984:                            response.setDateHeader("Expires", System
0985:                                    .currentTimeMillis()
0986:                                    + this .themeCacheTime);
0987:                            response.setHeader("Pragma", "cache"); // Required to apply
0988:                            // caching in some
0989:                            // Tomcats
0990:                        }
0991:                        // Write the data to client
0992:                        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
0993:                        int bytesRead = 0;
0994:                        OutputStream out = response.getOutputStream();
0995:                        while ((bytesRead = data.read(buffer)) > 0) {
0996:                            out.write(buffer, 0, bytesRead);
0997:                        }
0998:                        out.close();
0999:                        data.close();
1000:                    } else {
1001:                        response.sendError(HttpServletResponse.SC_NOT_FOUND);
1002:                    }
1003:
1004:                } catch (java.io.IOException e) {
1005:                    Log.info("Resource transfer failed:  "
1006:                            + request.getRequestURI() + ". (" + e.getMessage()
1007:                            + ")");
1008:                }
1009:
1010:                return true;
1011:            }
1012:
1013:            /** Get the variable map for the session */
1014:            private static synchronized HttpVariableMap getVariableMap(
1015:                    Application application, HttpServletRequest request) {
1016:
1017:                HttpSession session = request.getSession();
1018:
1019:                // Get the application to variablemap map
1020:                Map varMapMap = (Map) session.getAttribute(SESSION_ATTR_VARMAP);
1021:                if (varMapMap == null) {
1022:                    varMapMap = new WeakHashMap();
1023:                    session.setAttribute(SESSION_ATTR_VARMAP, varMapMap);
1024:                }
1025:
1026:                // Create a variable map, if it does not exists.
1027:                HttpVariableMap variableMap = (HttpVariableMap) varMapMap
1028:                        .get(application);
1029:                if (variableMap == null) {
1030:                    variableMap = new HttpVariableMap();
1031:                    varMapMap.put(application, variableMap);
1032:                }
1033:
1034:                return variableMap;
1035:            }
1036:
1037:            /** Get the current application URL from request */
1038:            private URL getApplicationUrl(HttpServletRequest request)
1039:                    throws MalformedURLException {
1040:
1041:                URL applicationUrl;
1042:                try {
1043:                    URL reqURL = new URL((request.isSecure() ? "https://"
1044:                            : "http://")
1045:                            + request.getServerName()
1046:                            + ":"
1047:                            + request.getServerPort() + request.getRequestURI());
1048:                    String servletPath = request.getContextPath()
1049:                            + request.getServletPath();
1050:                    if (servletPath.length() == 0
1051:                            || servletPath.charAt(servletPath.length() - 1) != '/')
1052:                        servletPath = servletPath + "/";
1053:                    applicationUrl = new URL(reqURL, servletPath);
1054:                } catch (MalformedURLException e) {
1055:                    Log.error("Error constructing application url "
1056:                            + request.getRequestURI() + " (" + e + ")");
1057:                    throw e;
1058:                }
1059:
1060:                return applicationUrl;
1061:            }
1062:
1063:            /**
1064:             * Get the existing application for given request. Looks for application
1065:             * instance for given request based on the requested URL.
1066:             * 
1067:             * @param request
1068:             *            HTTP request
1069:             * @return Application instance, or null if the URL does not map to valid
1070:             *         application.
1071:             */
1072:            private Application getApplication(HttpServletRequest request)
1073:                    throws MalformedURLException {
1074:
1075:                // Ensure that the session is still valid
1076:                HttpSession session = request.getSession(false);
1077:                if (session == null)
1078:                    return null;
1079:
1080:                // Get application list for the session.
1081:                LinkedList applications = (LinkedList) session
1082:                        .getAttribute(SESSION_ATTR_APPS);
1083:                if (applications == null)
1084:                    return null;
1085:
1086:                // Search for the application (using the application URI) from the list
1087:                Application application = null;
1088:                for (Iterator i = applications.iterator(); i.hasNext()
1089:                        && application == null;) {
1090:                    Application a = (Application) i.next();
1091:                    String aPath = a.getURL().getPath();
1092:                    String servletPath = request.getContextPath()
1093:                            + request.getServletPath();
1094:                    if (servletPath.length() < aPath.length())
1095:                        servletPath += "/";
1096:                    if (servletPath.equals(aPath))
1097:                        application = a;
1098:                }
1099:
1100:                // Remove stopped applications from the list
1101:                if (application != null && !application.isRunning()) {
1102:                    applications.remove(application);
1103:                    application = null;
1104:                }
1105:
1106:                return application;
1107:            }
1108:
1109:            /**
1110:             * Create a new application.
1111:             * 
1112:             * @return New application instance
1113:             */
1114:            private Application createApplication(HttpServletRequest request)
1115:                    throws MalformedURLException, InstantiationException,
1116:                    IllegalAccessException {
1117:
1118:                Application application = null;
1119:
1120:                // Get the application url
1121:                URL applicationUrl = getApplicationUrl(request);
1122:
1123:                // Get application list.
1124:                HttpSession session = request.getSession();
1125:                if (session == null)
1126:                    return null;
1127:                LinkedList applications = (LinkedList) session
1128:                        .getAttribute(SESSION_ATTR_APPS);
1129:                if (applications == null) {
1130:                    applications = new LinkedList();
1131:                    session.setAttribute(SESSION_ATTR_APPS, applications);
1132:                    HttpSessionBindingListener sessionBindingListener = new SessionBindingListener(
1133:                            applications);
1134:                    session.setAttribute(SESSION_BINDING_LISTENER,
1135:                            sessionBindingListener);
1136:                }
1137:
1138:                // Create new application and start it
1139:                try {
1140:                    application = (Application) this .applicationClass
1141:                            .newInstance();
1142:                    applications.add(application);
1143:                    application
1144:                            .addListener((Application.WindowAttachListener) this );
1145:                    application
1146:                            .addListener((Application.WindowDetachListener) this );
1147:                    application.setLocale(request.getLocale());
1148:
1149:                    // Get application context for this session
1150:                    WebApplicationContext context = (WebApplicationContext) session
1151:                            .getAttribute(SESSION_ATTR_CONTEXT);
1152:                    if (context == null) {
1153:                        context = new WebApplicationContext(session);
1154:                        session.setAttribute(SESSION_ATTR_CONTEXT, context);
1155:                    }
1156:
1157:                    application.start(applicationUrl,
1158:                            this .applicationProperties, context);
1159:
1160:                } catch (IllegalAccessException e) {
1161:                    Log.error("Illegal access to application class "
1162:                            + this .applicationClass.getName());
1163:                    throw e;
1164:                } catch (InstantiationException e) {
1165:                    Log.error("Failed to instantiate application class: "
1166:                            + this .applicationClass.getName());
1167:                    throw e;
1168:                }
1169:
1170:                return application;
1171:            }
1172:
1173:            /** End application */
1174:            private void endApplication(HttpServletRequest request,
1175:                    HttpServletResponse response, Application application)
1176:                    throws IOException {
1177:
1178:                String logoutUrl = application.getLogoutURL();
1179:                if (logoutUrl == null)
1180:                    logoutUrl = application.getURL().toString();
1181:
1182:                HttpSession session = request.getSession();
1183:                if (session != null) {
1184:                    LinkedList applications = (LinkedList) session
1185:                            .getAttribute(SESSION_ATTR_APPS);
1186:                    if (applications != null)
1187:                        applications.remove(application);
1188:                }
1189:
1190:                response.sendRedirect(response.encodeRedirectURL(logoutUrl));
1191:            }
1192:
1193:            /**
1194:             * Get the existing application or create a new one. Get a window within an
1195:             * application based on the requested URI.
1196:             * 
1197:             * @param request
1198:             *            HTTP Request.
1199:             * @param application
1200:             *            Application to query for window.
1201:             * @return Window mathing the given URI or null if not found.
1202:             */
1203:            private Window getApplicationWindow(HttpServletRequest request,
1204:                    Application application) throws ServletException {
1205:
1206:                Window window = null;
1207:
1208:                // Find the window where the request is handled
1209:                String path = request.getPathInfo();
1210:
1211:                // Main window as the URI is empty
1212:                if (path == null || path.length() == 0 || path.equals("/"))
1213:                    window = application.getMainWindow();
1214:
1215:                // Try to search by window name
1216:                else {
1217:                    String windowName = null;
1218:                    if (path.charAt(0) == '/')
1219:                        path = path.substring(1);
1220:                    int index = path.indexOf('/');
1221:                    if (index < 0) {
1222:                        windowName = path;
1223:                        path = "";
1224:                    } else {
1225:                        windowName = path.substring(0, index);
1226:                        path = path.substring(index + 1);
1227:                    }
1228:                    window = application.getWindow(windowName);
1229:
1230:                    if (window == null) {
1231:
1232:                        // If the window has existed, and is now removed
1233:                        // send a blank page
1234:                        if (allWindows.contains(windowName))
1235:                            return null;
1236:
1237:                        // By default, we use main window
1238:                        window = application.getMainWindow();
1239:                    } else if (!window.isVisible()) {
1240:
1241:                        // Implicitly painting without actually invoking paint()
1242:                        window.requestRepaintRequests();
1243:
1244:                        // If the window is invisible send a blank page
1245:                        return null;
1246:                    }
1247:                }
1248:
1249:                // Create and open new debug window for application if requested
1250:                if (this .debugMode
1251:                        && application.getWindow(DebugWindow.WINDOW_NAME) == null)
1252:                    try {
1253:                        DebugWindow debugWindow = new DebugWindow(application,
1254:                                request.getSession(false), this );
1255:                        debugWindow.setWidth(370);
1256:                        debugWindow.setHeight(480);
1257:                        application.addWindow(debugWindow);
1258:                    } catch (Exception e) {
1259:                        throw new ServletException(
1260:                                "Failed to create debug window for application",
1261:                                e);
1262:                    }
1263:
1264:                return window;
1265:            }
1266:
1267:            /**
1268:             * Get relative location of a theme resource.
1269:             * 
1270:             * @param theme
1271:             *            Theme name
1272:             * @param resource
1273:             *            Theme resource
1274:             * @return External URI specifying the resource
1275:             */
1276:            public String getResourceLocation(String theme,
1277:                    ThemeResource resource) {
1278:
1279:                if (resourcePath == null)
1280:                    return resource.getResourceId();
1281:                return resourcePath + theme + "/" + resource.getResourceId();
1282:            }
1283:
1284:            /**
1285:             * Check if web adapter is in debug mode. Extra output is generated to log
1286:             * when debug mode is enabled.
1287:             * 
1288:             * @return Debug mode
1289:             */
1290:            public boolean isDebugMode() {
1291:                return debugMode;
1292:            }
1293:
1294:            /**
1295:             * Returns the theme source.
1296:             * 
1297:             * @return ThemeSource
1298:             */
1299:            public ThemeSource getThemeSource() {
1300:                return themeSource;
1301:            }
1302:
1303:            protected void addDirtyWindow(Application application, Window window) {
1304:                synchronized (applicationToDirtyWindowSetMap) {
1305:                    HashSet dirtyWindows = (HashSet) applicationToDirtyWindowSetMap
1306:                            .get(application);
1307:                    if (dirtyWindows == null) {
1308:                        dirtyWindows = new HashSet();
1309:                        applicationToDirtyWindowSetMap.put(application,
1310:                                dirtyWindows);
1311:                    }
1312:                    dirtyWindows.add(window);
1313:                }
1314:            }
1315:
1316:            protected void removeDirtyWindow(Application application,
1317:                    Window window) {
1318:                synchronized (applicationToDirtyWindowSetMap) {
1319:                    HashSet dirtyWindows = (HashSet) applicationToDirtyWindowSetMap
1320:                            .get(application);
1321:                    if (dirtyWindows != null)
1322:                        dirtyWindows.remove(window);
1323:                }
1324:            }
1325:
1326:            /**
1327:             * @see org.millstone.base.Application.WindowAttachListener#windowAttached(Application.WindowAttachEvent)
1328:             */
1329:            public void windowAttached(WindowAttachEvent event) {
1330:                Window win = event.getWindow();
1331:                win.addListener((Paintable.RepaintRequestListener) this );
1332:
1333:                // Add to window names
1334:                allWindows.add(win.getName());
1335:
1336:                // Add window to dirty window references if it is visible
1337:                // Or request the window to pass on the repaint requests
1338:                if (win.isVisible())
1339:                    addDirtyWindow(event.getApplication(), win);
1340:                else
1341:                    win.requestRepaintRequests();
1342:
1343:            }
1344:
1345:            /**
1346:             * @see org.millstone.base.Application.WindowDetachListener#windowDetached(Application.WindowDetachEvent)
1347:             */
1348:            public void windowDetached(WindowDetachEvent event) {
1349:                event.getWindow().removeListener(
1350:                        (Paintable.RepaintRequestListener) this );
1351:
1352:                // Add dirty window reference for closing the window
1353:                addDirtyWindow(event.getApplication(), event.getWindow());
1354:            }
1355:
1356:            /**
1357:             * @see org.millstone.base.terminal.Paintable.RepaintRequestListener#repaintRequested(Paintable.RepaintRequestEvent)
1358:             */
1359:            public void repaintRequested(RepaintRequestEvent event) {
1360:
1361:                Paintable p = event.getPaintable();
1362:                Application app = null;
1363:                if (p instanceof  Window)
1364:                    app = ((Window) p).getApplication();
1365:
1366:                if (app != null)
1367:                    addDirtyWindow(app, ((Window) p));
1368:
1369:                Object lock = applicationToServerCommandStreamLock.get(app);
1370:                if (lock != null)
1371:                    synchronized (lock) {
1372:                        lock.notifyAll();
1373:                    }
1374:            }
1375:
1376:            /** Get the list of dirty windows in application */
1377:            protected Set getDirtyWindows(Application app) {
1378:                HashSet dirtyWindows;
1379:                synchronized (applicationToDirtyWindowSetMap) {
1380:                    dirtyWindows = (HashSet) applicationToDirtyWindowSetMap
1381:                            .get(app);
1382:                }
1383:                return dirtyWindows;
1384:            }
1385:
1386:            /** Remove a window from the list of dirty windows */
1387:            private void windowPainted(Application app, Window window) {
1388:                removeDirtyWindow(app, window);
1389:            }
1390:
1391:            /**
1392:             * Generate server commands stream. If the server commands are not
1393:             * requested, return false
1394:             */
1395:            private boolean handleServerCommands(HttpServletRequest request,
1396:                    HttpServletResponse response) {
1397:
1398:                // Server commands are allways requested with certain parameter
1399:                if (request.getParameter(SERVER_COMMAND_PARAM) == null)
1400:                    return false;
1401:
1402:                // Get the application
1403:                Application application;
1404:                try {
1405:                    application = getApplication(request);
1406:                } catch (MalformedURLException e) {
1407:                    return false;
1408:                }
1409:                if (application == null)
1410:                    return false;
1411:
1412:                // Create continuous server commands stream
1413:                try {
1414:
1415:                    // Writer for writing the stream
1416:                    PrintWriter w = new PrintWriter(response.getOutputStream());
1417:
1418:                    // Print necessary http page headers and padding
1419:                    w.println("<html><head></head><body>");
1420:                    for (int i = 0; i < SERVER_COMMAND_HEADER_PADDING; i++)
1421:                        w.print(' ');
1422:
1423:                    // Clock for synchronizing the stream
1424:                    Object lock = new Object();
1425:                    synchronized (applicationToServerCommandStreamLock) {
1426:                        Object oldlock = applicationToServerCommandStreamLock
1427:                                .get(application);
1428:                        if (oldlock != null)
1429:                            synchronized (oldlock) {
1430:                                oldlock.notifyAll();
1431:                            }
1432:                        applicationToServerCommandStreamLock.put(application,
1433:                                lock);
1434:                    }
1435:                    while (applicationToServerCommandStreamLock
1436:                            .get(application) == lock
1437:                            && application.isRunning()) {
1438:                        synchronized (application) {
1439:
1440:                            // Session expiration
1441:                            Date lastRequest = (Date) applicationToLastRequestDate
1442:                                    .get(application);
1443:                            if (lastRequest != null
1444:                                    && lastRequest.getTime()
1445:                                            + request.getSession()
1446:                                                    .getMaxInactiveInterval()
1447:                                            * 1000 < System.currentTimeMillis()) {
1448:
1449:                                // Session expired, close application
1450:                                application.close();
1451:                            } else {
1452:
1453:                                // Application still alive - keep updating windows
1454:                                Set dws = getDirtyWindows(application);
1455:                                if (dws != null && !dws.isEmpty()) {
1456:
1457:                                    // For one of the dirty windows (in each
1458:                                    // application)
1459:                                    // request redraw
1460:                                    Window win = (Window) dws.iterator().next();
1461:                                    w
1462:                                            .println("<script>\n"
1463:                                                    + ThemeFunctionLibrary
1464:                                                            .getWindowRefreshScript(
1465:                                                                    application,
1466:                                                                    win,
1467:                                                                    WebBrowserProbe
1468:                                                                            .getTerminalType(request
1469:                                                                                    .getSession()))
1470:                                                    + "</script>");
1471:
1472:                                    removeDirtyWindow(application, win);
1473:
1474:                                    // Windows that are closed immediately are "painted"
1475:                                    // now
1476:                                    if (win.getApplication() == null
1477:                                            || !win.isVisible())
1478:                                        win.requestRepaintRequests();
1479:                                }
1480:                            }
1481:                        }
1482:
1483:                        // Send the generated commands and newline immediately to
1484:                        // browser
1485:                        w.println(" ");
1486:                        w.flush();
1487:                        response.flushBuffer();
1488:
1489:                        synchronized (lock) {
1490:                            try {
1491:                                lock
1492:                                        .wait(SERVER_COMMAND_STREAM_MAINTAIN_PERIOD);
1493:                            } catch (InterruptedException ignored) {
1494:                            }
1495:                        }
1496:                    }
1497:                } catch (IOException ignore) {
1498:
1499:                    // In case of an Exceptions the server command stream is
1500:                    // terminated
1501:                    synchronized (applicationToServerCommandStreamLock) {
1502:                        if (applicationToServerCommandStreamLock
1503:                                .get(application) == application)
1504:                            applicationToServerCommandStreamLock
1505:                                    .remove(application);
1506:                    }
1507:                }
1508:
1509:                return true;
1510:            }
1511:
1512:            private class SessionBindingListener implements 
1513:                    HttpSessionBindingListener {
1514:                private LinkedList applications;
1515:
1516:                protected SessionBindingListener(LinkedList applications) {
1517:                    this .applications = applications;
1518:                }
1519:
1520:                /**
1521:                 * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
1522:                 */
1523:                public void valueBound(HttpSessionBindingEvent arg0) {
1524:                    // We are not interested in bindings
1525:                }
1526:
1527:                /**
1528:                 * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
1529:                 */
1530:                public void valueUnbound(HttpSessionBindingEvent event) {
1531:
1532:                    // If the binding listener is unbound from the session, the
1533:                    // session must be closing
1534:                    if (event.getName().equals(SESSION_BINDING_LISTENER)) {
1535:
1536:                        // Close all applications
1537:                        Object[] apps = applications.toArray();
1538:                        for (int i = 0; i < apps.length; i++) {
1539:                            if (apps[i] != null) {
1540:
1541:                                // Close app
1542:                                ((Application) apps[i]).close();
1543:
1544:                                // Stop application server commands stream
1545:                                Object lock = applicationToServerCommandStreamLock
1546:                                        .get(apps[i]);
1547:                                if (lock != null)
1548:                                    synchronized (lock) {
1549:                                        lock.notifyAll();
1550:                                    }
1551:                                applicationToServerCommandStreamLock
1552:                                        .remove(apps[i]);
1553:
1554:                                // Remove application from applications list
1555:                                applications.remove(apps[i]);
1556:                            }
1557:                        }
1558:                    }
1559:                }
1560:
1561:            }
1562:
1563:            /** Implementation of ParameterHandler.ErrorEvent interface. */
1564:            public class ParameterHandlerErrorImpl implements 
1565:                    ParameterHandler.ErrorEvent {
1566:
1567:                private ParameterHandler owner;
1568:                private Throwable throwable;
1569:
1570:                private ParameterHandlerErrorImpl(ParameterHandler owner,
1571:                        Throwable throwable) {
1572:                    this .owner = owner;
1573:                    this .throwable = throwable;
1574:                }
1575:
1576:                /**
1577:                 * @see org.millstone.base.terminal.Terminal.ErrorEvent#getThrowable()
1578:                 */
1579:                public Throwable getThrowable() {
1580:                    return this .throwable;
1581:                }
1582:
1583:                /**
1584:                 * @see org.millstone.base.terminal.ParameterHandler.ErrorEvent#getParameterHandler()
1585:                 */
1586:                public ParameterHandler getParameterHandler() {
1587:                    return this .owner;
1588:                }
1589:
1590:            }
1591:
1592:            /** Implementation of URIHandler.ErrorEvent interface. */
1593:            public class URIHandlerErrorImpl implements  URIHandler.ErrorEvent {
1594:
1595:                private URIHandler owner;
1596:                private Throwable throwable;
1597:
1598:                private URIHandlerErrorImpl(URIHandler owner,
1599:                        Throwable throwable) {
1600:                    this .owner = owner;
1601:                    this .throwable = throwable;
1602:                }
1603:
1604:                /**
1605:                 * @see org.millstone.base.terminal.Terminal.ErrorEvent#getThrowable()
1606:                 */
1607:                public Throwable getThrowable() {
1608:                    return this .throwable;
1609:                }
1610:
1611:                /**
1612:                 * @see org.millstone.base.terminal.URIHandler.ErrorEvent#getURIHandler()
1613:                 */
1614:                public URIHandler getURIHandler() {
1615:                    return this.owner;
1616:                }
1617:            }
1618:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.