Source Code Cross Referenced for Servlets.java in  » Ajax » zk » org » zkoss » web » servlet » 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 » Ajax » zk » org.zkoss.web.servlet 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /* Servlets.java
002:
003:        {{IS_NOTE
004:        	Purpose:
005:        	Description:
006:        	History:
007:        	90/12/10 22:24:28, Create, Tom M. Yeh.
008:        }}IS_NOTE
009:
010:        Copyright (C) 2001 Potix Corporation. All Rights Reserved.
011:
012:        {{IS_RIGHT
013:        	This program is distributed under GPL Version 2.0 in the hope that
014:        	it will be useful, but WITHOUT ANY WARRANTY.
015:        }}IS_RIGHT
016:         */
017:        package org.zkoss.web.servlet;
018:
019:        import java.util.List;
020:        import java.util.LinkedList;
021:        import java.util.ArrayList;
022:        import java.util.Iterator;
023:        import java.util.Map;
024:        import java.util.HashMap;
025:        import java.util.Collections;
026:        import java.util.Locale;
027:        import java.util.Properties;
028:        import java.io.InputStream;
029:        import java.io.IOException;
030:        import java.io.StringWriter;
031:        import java.io.PrintWriter;
032:        import java.io.UnsupportedEncodingException;
033:        import java.net.URL;
034:        import java.net.MalformedURLException;
035:
036:        import javax.servlet.ServletContext;
037:        import javax.servlet.ServletRequest;
038:        import javax.servlet.ServletResponse;
039:        import javax.servlet.ServletException;
040:        import javax.servlet.RequestDispatcher;
041:        import javax.servlet.http.HttpSession;
042:        import javax.servlet.http.HttpServletRequest;
043:
044:        import org.zkoss.mesg.MCommon;
045:        import org.zkoss.lang.D;
046:        import org.zkoss.lang.Objects;
047:        import org.zkoss.lang.Classes;
048:        import org.zkoss.lang.Exceptions;
049:        import org.zkoss.lang.SystemException;
050:        import org.zkoss.util.Checksums;
051:        import org.zkoss.util.CacheMap;
052:        import org.zkoss.util.CollectionsX;
053:        import org.zkoss.util.Locales;
054:        import org.zkoss.util.logging.Log;
055:        import org.zkoss.util.resource.Locator;
056:        import org.zkoss.util.resource.Locators;
057:        import org.zkoss.util.resource.PropertyBundle;
058:        import org.zkoss.idom.Element;
059:        import org.zkoss.idom.input.SAXBuilder;
060:
061:        import org.zkoss.web.Attributes;
062:        import org.zkoss.web.servlet.http.Encodes;
063:        import org.zkoss.web.util.resource.ExtendletContext;
064:        import org.zkoss.web.util.resource.ServletContextLocator;
065:
066:        /**
067:         * The servlet relevant utilities.
068:         *
069:         * @author tomyeh
070:         * @see org.zkoss.web.servlet.http.Https
071:         * @see org.zkoss.web.servlet.Charsets
072:         */
073:        public class Servlets {
074:            //	private static final Log log = Log.lookup(Servlets.class);
075:
076:            private static final boolean _svl24, _svl23;
077:            static {
078:                boolean b = false;
079:                try {
080:                    ServletResponse.class.getMethod("getContentType",
081:                            new Class[0]);
082:                    b = true;
083:                } catch (Throwable ex) {
084:                }
085:                _svl24 = b;
086:
087:                if (!b) {
088:                    try {
089:                        HttpSession.class.getMethod("getServletContext",
090:                                new Class[0]);
091:                        b = true;
092:                    } catch (Throwable ex) {
093:                    }
094:                }
095:                _svl23 = b;
096:            }
097:
098:            /** Utilities; no instantiation required. */
099:            protected Servlets() {
100:            }
101:
102:            /** Returns whether a URL starts with xxx://, mailto:, about:,
103:             * javascript:
104:             */
105:            public static final boolean isUniversalURL(String uri) {
106:                if (uri == null || uri.length() == 0)
107:                    return false;
108:
109:                final char cc = uri.charAt(0);
110:                return cc >= 'a'
111:                        && cc <= 'z'
112:                        && (uri.indexOf("://") > 0 || uri.startsWith("mailto:")
113:                                || uri.startsWith("javascript:") || uri
114:                                .startsWith("about:"));
115:            }
116:
117:            /** Returns whether the current Web server supports Servlet 2.4 or above.
118:             *
119:             * @since 3.0.0
120:             */
121:            public static final boolean isServlet24() {
122:                return _svl24;
123:            }
124:
125:            /** Returns whether the current Web server supports Servlet 2.3 or above.
126:             * Thus, if {@link #isServlet24} returns true, {@link #isServlet23}
127:             * must return true, too.
128:             *
129:             * @since 3.0.0
130:             */
131:            public static final boolean isServlet23() {
132:                return _svl23;
133:            }
134:
135:            //-- resource locator --//
136:            /** Locates a page based on the specified Locale. It never returns null.
137:             *
138:             * <p>If an URI contains "*", it will be replaced with a proper Locale.
139:             * For example, if the current Locale is zh_TW and the resource is
140:             * named "ab*.cd", then it searches "ab_zh_TW.cd", "ab_zh.cd" and
141:             * then "ab.cd", until any of them is found.
142:             *
143:             * <blockquote>Note: "*" must be right before ".", or the last character.
144:             * For example, "ab*.cd" and "ab*" are both correct, while
145:             * "ab*cd" and "ab*\/cd" are ignored.</blockquote>
146:             *
147:             * <p>If an URI contains two "*", the first "*" will be replaced with
148:             * a browser code and the second with a proper locale.
149:             * The browser code depends on what browser
150:             * the user are used to visit the web site.
151:             * Currently, the code for Internet Explorer is "ie", Safari is "saf",
152:             * Opera is "opr" and all others are "moz".
153:             * Thus, in the above example, if the resource is named "ab**.cd"
154:             * and Firefox is used, then it searches "abmoz_zh_TW.cd", "abmoz_zh.cd"
155:             * and then "abmoz.cd", until any of them is found.
156:             *
157:             * <p>Note: it assumes the path as name_lang_cn_var.ext where
158:             * ".ext" is optional. Example, my_zh_tw.html.jsp.
159:             *
160:             * <p>Note: unlike {@link Encodes#encodeURL(ServletContext, ServletRequest, ServletResponse, String)},
161:             * it always locates the Locale, without handling "*".
162:             *
163:             * @param ctx the servlet context to locate pages
164:             * @param pgpath the page path excluding servlet name. It is OK to have
165:             * the query string. It might contain "*" for current browser code and Locale.
166:             * @param locator the locator used to locate resource. If null, ctx
167:             * is assumed.
168:             * @return pgpath if the original one matches; others if locale matches;
169:             * never null
170:             */
171:            public static final String locate(ServletContext ctx,
172:                    ServletRequest request, String pgpath, Locator locator)
173:                    throws ServletException {
174:                if (isUniversalURL(pgpath))
175:                    return pgpath;
176:
177:                final int jquest = pgpath.indexOf('?');
178:                final int f = pgpath.indexOf('*');
179:                if (f < 0 || (jquest >= 0 && f > jquest))
180:                    return pgpath;
181:                //optimize the case that no "*" at all
182:
183:                final String qstr;
184:                if (jquest >= 0) {
185:                    qstr = pgpath.substring(jquest);
186:                    pgpath = pgpath.substring(0, jquest);
187:                } else {
188:                    qstr = null;
189:                }
190:
191:                //by browser?
192:                int l = pgpath.lastIndexOf('*');
193:                if (l > f) { //two '*'
194:                    final String bc = Servlets.isExplorer(request) ? "ie"
195:                            : Servlets.isSafari(request) ? "saf" : Servlets
196:                                    .isOpera(request) ? "opr" : "moz";
197:                    l += bc.length() - 1;
198:                    pgpath = pgpath.substring(0, f) + bc
199:                            + pgpath.substring(f + 1);
200:                }
201:
202:                //remove "*"
203:                pgpath = pgpath.substring(0, l) + pgpath.substring(l + 1); //remove
204:
205:                //by locale? 1) before the first dot, 2) the last char if no dot
206:                boolean byLocale = l == pgpath.length()
207:                        || (pgpath.charAt(l) == '.' && pgpath.indexOf('/',
208:                                l + 1) < 0);
209:                if (byLocale) {
210:                    //make sure no dot before it
211:                    for (int j = l; --j >= 0;) {
212:                        final char cc = pgpath.charAt(j);
213:                        if (cc == '.') {
214:                            byLocale = false;
215:                            break;
216:                        } else if (cc == '/') {
217:                            break;
218:                        }
219:                    }
220:                }
221:                if (!byLocale)
222:                    return qstr != null ? pgpath + qstr : pgpath; //not by locale
223:
224:                final String PGPATH_CACHE = "s_pgpath_cache";
225:                Map map = (Map) ctx.getAttribute(PGPATH_CACHE);
226:                if (map == null) {
227:                    map = Collections.synchronizedMap( //10 min
228:                            new CacheMap(500, 10 * 60 * 1000));
229:                    ctx.setAttribute(PGPATH_CACHE, map);
230:                }
231:
232:                final Locale locale = Locales.getCurrent();
233:                final URIIndex index = new URIIndex(pgpath, locale);
234:
235:                String uri = (String) map.get(index);
236:                if (uri == null) {
237:                    final Locators.URLLocation loc = Locators.locate(pgpath,
238:                            locale, locator != null ? locator
239:                                    : new ServletContextLocator(ctx));
240:                    uri = loc != null ? loc.file : pgpath;
241:                    map.put(index, uri);
242:                }
243:
244:                return qstr != null ? uri + qstr : uri;
245:            }
246:
247:            private static class URIIndex {
248:                private final String _uri;
249:                private final Locale _locale;
250:
251:                private URIIndex(String uri, Locale locale) {
252:                    if (uri == null || locale == null)
253:                        throw new IllegalArgumentException("null");
254:                    _uri = uri;
255:                    _locale = locale;
256:                }
257:
258:                public int hashCode() {
259:                    return _uri.hashCode();
260:                }
261:
262:                public boolean equals(Object o) {
263:                    //To speed up, don't check whether o is the right class
264:                    final URIIndex idx = (URIIndex) o;
265:                    return _uri.equals(idx._uri) && _locale.equals(idx._locale);
266:                }
267:            }
268:
269:            /** Returns whether the client is a robot (such as Web crawlers).
270:             *
271:             * <p>Because there are too many robots, it returns true if the user-agent
272:             * is not recognized.
273:             */
274:            public static final boolean isRobot(ServletRequest req) {
275:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
276:                        .getHeader("user-agent")
277:                        : null;
278:                if (agt == null)
279:                    return false;
280:
281:                agt = agt.toLowerCase();
282:                return agt.indexOf("msie") < 0 && agt.indexOf("opera") < 0
283:                        && agt.indexOf("gecko/") < 0
284:                        && agt.indexOf("safari") < 0;
285:            }
286:
287:            /** Returns whether the browser is Internet Explorer.
288:             * If true, it also implies {@link #isExplorer7} is true.
289:             */
290:            public static final boolean isExplorer(ServletRequest req) {
291:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
292:                        .getHeader("user-agent")
293:                        : null;
294:                if (agt == null)
295:                    return false;
296:
297:                agt = agt.toLowerCase();
298:                return agt.indexOf("msie") >= 0 && agt.indexOf("opera") < 0;
299:            }
300:
301:            /** Returns whether the browser is Explorer 7 or later.
302:             */
303:            public static final boolean isExplorer7(ServletRequest req) {
304:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
305:                        .getHeader("user-agent")
306:                        : null;
307:                if (agt == null)
308:                    return false;
309:
310:                agt = agt.toLowerCase();
311:                return agt.indexOf("msie 7") >= 0;
312:            }
313:
314:            /** Returns whether the browser is Gecko based, such as Mozilla, Firefox and Camino
315:             */
316:            public static final boolean isGecko(ServletRequest req) {
317:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
318:                        .getHeader("user-agent")
319:                        : null;
320:                if (agt == null)
321:                    return false;
322:
323:                agt = agt.toLowerCase();
324:                return agt.indexOf("gecko/") >= 0 && agt.indexOf("safari") < 0;
325:            }
326:
327:            /** Returns whether the browser is Safari.
328:             */
329:            public static final boolean isSafari(ServletRequest req) {
330:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
331:                        .getHeader("user-agent")
332:                        : null;
333:                if (agt == null)
334:                    return false;
335:
336:                agt = agt.toLowerCase();
337:                return agt.indexOf("safari") >= 0;
338:            }
339:
340:            /** Returns whether the browser is Opera.
341:             */
342:            public static final boolean isOpera(ServletRequest req) {
343:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
344:                        .getHeader("user-agent")
345:                        : null;
346:                if (agt == null)
347:                    return false;
348:
349:                agt = agt.toLowerCase();
350:                return agt.indexOf("opera") >= 0;
351:            }
352:
353:            /** Returns whether the client is a mobile device supporting MIL
354:             * (Mobile Interactive Language).
355:             * @since 2.4.0
356:             */
357:            public static final boolean isMilDevice(ServletRequest req) {
358:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
359:                        .getHeader("user-agent")
360:                        : null;
361:                if (agt == null)
362:                    return false;
363:
364:                //ZK Mobile/1.0 (RMIL)
365:                agt = agt.toLowerCase();
366:                return agt.indexOf("zk") >= 0 && agt.indexOf("rmil") >= 0;
367:            }
368:
369:            /** Returns whether the client is a mobile device supporting HIL
370:             * (Handset Interactive Language).
371:             *
372:             * <p>Note: ZK Mobile for Android supports both MIL and HIL.
373:             * That is, both {@link #isHilDevice} and {@link #isMilDevice}
374:             * return true.
375:             *
376:             * @since 3.0.2
377:             */
378:            public static final boolean isHilDevice(ServletRequest req) {
379:                String agt = req instanceof  HttpServletRequest ? ((HttpServletRequest) req)
380:                        .getHeader("user-agent")
381:                        : null;
382:                if (agt == null)
383:                    return false;
384:
385:                //ZK Mobile for Android 1.0 (RMIL; RHIL)
386:                agt = agt.toLowerCase();
387:                return agt.indexOf("zk") >= 0 && agt.indexOf("rhil") >= 0;
388:            }
389:
390:            /** Returns the user-agent header, which indicates what the client is,
391:             * or an empty string if not available.
392:             *
393:             * <p>Note: it doesn't return null, so it is easy to test what
394:             * the client is with {@link String#indexOf}.
395:             *
396:             * @since 3.0.2
397:             */
398:            public static final String getUserAgent(ServletRequest req) {
399:                if (req instanceof  HttpServletRequest) {
400:                    final String s = ((HttpServletRequest) req)
401:                            .getHeader("user-agent");
402:                    if (s != null)
403:                        return s;
404:                }
405:                return "";
406:            }
407:
408:            /**
409:             * Tests whether this page is included by another page.
410:             */
411:            public static final boolean isIncluded(ServletRequest request) {
412:                return request.getAttribute(Attributes.INCLUDE_CONTEXT_PATH) != null;
413:            }
414:
415:            /**
416:             * Tests whether this page is forwarded by another page.
417:             */
418:            public static final boolean isForwarded(ServletRequest request) {
419:                return request.getAttribute(Attributes.FORWARD_CONTEXT_PATH) != null;
420:            }
421:
422:            /**
423:             * Forward to the specified URI.
424:             * It enhances RequestDispatcher in the following ways.
425:             *
426:             * <ul>
427:             *  <li>It handles "~ctx"" where ctx is the context path of the
428:             *  foreign context. It is called foriegn URI.</li>
429:             *  <li>It detects whether the page calling this method
430:             * is included by another servlet/page. If so, it uses
431:             * RequestDispatcher.include() instead of RequestDispatcher.forward().</li>
432:             *  <li>The forwarded page could accept additional parameters --
433:             * acutually converting parameters to a query string
434:             * and appending it to uri.</li>
435:             * <li>In additions, it does HTTP encoding, i.e., converts '+' and other
436:             * characters to comply HTTP.</li>
437:             * </ul>
438:             *
439:             * <p>NOTE: don't include query parameters in uri.
440:             *
441:             * @param ctx the servlet context. If null, uri cannot be foreign URI.
442:             * It is ignored if URI is relevant (neither starts with '/' nor '~').
443:             * @param uri the URI to include. It is OK to relevant (without leading
444:             * '/'). If starts with "/", the context path of request is assumed.
445:             * To reference to foreign context, use "~ctx" where ctx is the
446:             * context path of the foreign context (without leading '/').
447:             * If it could be any context path recognized by the Web container or
448:             * any name registered with {@link #addExtendletContext}.
449:             * @param params the parameter map; null to ignore
450:             * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM},
451:             * and {@link #APPEND_PARAM}. It defines how to handle if both uri
452:             * and params contains the same parameter.
453:             */
454:            public static final void forward(ServletContext ctx,
455:                    ServletRequest request, ServletResponse response,
456:                    String uri, Map params, int mode) throws IOException,
457:                    ServletException {
458:                //		if (D.ON && log.debugable()) log.debug("Forwarding "+uri);
459:
460:                //include or foward depending whether this page is included or not
461:                if (isIncluded(request)) {
462:                    include(ctx, request, response, uri, params, mode);
463:                    return;
464:                }
465:
466:                final RequestDispatcher disp = getRequestDispatcher(ctx,
467:                        request, uri, params, mode);
468:                if (disp == null)
469:                    throw new ServletException(
470:                            "No dispatcher available to forward to " + uri);
471:
472:                if (mode == PASS_THRU_ATTR && params != null
473:                        && !params.isEmpty()) {
474:                    final Map old = setPassThruAttr(request, params);
475:                    try {
476:                        disp.forward(request, response);
477:                    } catch (ClassCastException ex) {
478:                        //Tom M. Yeh, 2006/09/21: Bug 1548478
479:                        //Cause: http://issues.apache.org/bugzilla/show_bug.cgi?id=39417
480:                        //
481:                        //Bug or limitation of Catalina: not accepting HttpServletRequest
482:                        //othere than the original one or wrapper of original one
483:                        //
484:                        //Real Cause: org.apache.catalina.core.ApplicationDispatcher
485:                        //call unwrapRequest() twice, and then causes ClassCastException
486:                        //
487:                        //Resolution: since it is the almost last statement, it is safe
488:                        //to ignore this exception
489:                        if (!(request instanceof  org.zkoss.web.portlet.RenderHttpServletRequest))
490:                            throw ex; //not the case described above
491:                    } finally {
492:                        restorePassThruAttr(request, old);
493:                    }
494:                } else {
495:                    disp.forward(request, response);
496:                }
497:            }
498:
499:            /** A shortcut of forward(request, response, uri, null, 0).
500:             */
501:            public static final void forward(ServletContext ctx,
502:                    ServletRequest request, ServletResponse response, String uri)
503:                    throws IOException, ServletException {
504:                forward(ctx, request, response, uri, null, 0);
505:            }
506:
507:            /**
508:             * Includes the resource at the specified URI.
509:             * It enhances RequestDispatcher to allow the inclusion with
510:             * a parameter map -- acutually converting parameters to a query string
511:             * and appending it to uri.
512:             *
513:             * <p>NOTE: don't include query parameters in uri.
514:             *
515:             * @param ctx the servlet context. If null, uri cannot be foreign URI.
516:             * It is ignored if URI is relevant (neither starts with '/' nor '~').
517:             * @param uri the URI to include. It is OK to relevant (without leading
518:             * '/'). If starts with "/", the context path of request is assumed.
519:             * To reference to foreign context, use "~ctx/" where ctx is the
520:             * context path of the foreign context (without leading '/').
521:             * @param params the parameter map; null to ignore
522:             * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM},
523:             * and {@link #APPEND_PARAM}. It defines how to handle if both uri
524:             * and params contains the same parameter.
525:             */
526:            public static final void include(ServletContext ctx,
527:                    ServletRequest request, ServletResponse response,
528:                    String uri, Map params, int mode) throws IOException,
529:                    ServletException {
530:                //		if (D.ON && log.debugable()) log.debug("Including "+uri+" at "+ctx);
531:
532:                //20050606: Tom Yeh
533:                //We have to set this special attribute for jetty
534:                //Otherwise, if including a page crossing context might not return
535:                //the same session
536:                request.setAttribute(
537:                        "org.mortbay.jetty.servlet.Dispatcher.shared_session",
538:                        Boolean.TRUE);
539:
540:                final RequestDispatcher disp = getRequestDispatcher(ctx,
541:                        request, uri, params, mode);
542:                if (disp == null)
543:                    throw new ServletException(
544:                            "No dispatcher available to include " + uri);
545:
546:                if (mode == PASS_THRU_ATTR && params != null
547:                        && !params.isEmpty()) {
548:                    final Map old = setPassThruAttr(request, params);
549:                    try {
550:                        disp.include(request, response);
551:                    } finally {
552:                        restorePassThruAttr(request, old);
553:                    }
554:                } else {
555:                    disp.include(request, response);
556:                }
557:            }
558:
559:            /** A shortcut of include(request, response, uri, null, 0).
560:             */
561:            public static final void include(ServletContext ctx,
562:                    ServletRequest request, ServletResponse response, String uri)
563:                    throws IOException, ServletException {
564:                include(ctx, request, response, uri, null, 0);
565:            }
566:
567:            /** Sets the arg attribute to pass parameters thru request's attribute.
568:             */
569:            private static final Map setPassThruAttr(ServletRequest request,
570:                    Map params) {
571:                final Map old = (Map) request.getAttribute(Attributes.ARG);
572:                request.setAttribute(Attributes.ARG, params);
573:                return old;
574:            }
575:
576:            /** Restores what has been done by {@link #setPassThruAttr}.
577:             */
578:            private static final void restorePassThruAttr(
579:                    ServletRequest request, Map old) {
580:                if (old != null)
581:                    request.setAttribute(Attributes.ARG, old);
582:                else
583:                    request.removeAttribute(Attributes.ARG);
584:            }
585:
586:            /** Returns the request dispatch of the specified URI.
587:             *
588:             * @param ctx the servlet context. If null, uri cannot be foreign URI.
589:             * It is ignored if uri is relevant (neither starts with '/' nor '~').
590:             * @param request the request. If null, uri cannot be relevant.
591:             * It is used only if uri is relevant.
592:             * @param uri the URI to include. It is OK to relevant (without leading
593:             * '/'). If starts with "/", the context path of request is assumed.
594:             * To reference to foreign context, use "~ctx/" where ctx is the
595:             * context path of the foreign context (without leading '/').
596:             * @param params the parameter map; null to ignore
597:             * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM},
598:             * and {@link #APPEND_PARAM}. It defines how to handle if both uri
599:             * and params contains the same parameter.
600:             */
601:            public static final RequestDispatcher getRequestDispatcher(
602:                    ServletContext ctx, ServletRequest request, String uri,
603:                    Map params, int mode) throws ServletException {
604:                final char cc = uri.length() > 0 ? uri.charAt(0) : (char) 0;
605:                if (ctx == null || (cc != '/' && cc != '~')) {//... or relevant
606:                    if (request == null)
607:                        throw new IllegalArgumentException(
608:                                ctx == null ? "Servlet context and request cannot be both null"
609:                                        : "Request is required to use revalant URI: "
610:                                                + uri);
611:                    if (cc == '~')
612:                        throw new IllegalArgumentException(
613:                                "Servlet context is required to use foreign URI: "
614:                                        + uri);
615:                    uri = generateURI(uri, params, mode);
616:                    return request.getRequestDispatcher(uri);
617:                }
618:
619:                //NO NEED to encodeURL since it is forward/include
620:                return new ParsedURI(ctx, uri).getRequestDispatcher(params,
621:                        mode);
622:            }
623:
624:            /** Returns the resource of the specified uri.
625:             * Unlike ServletContext.getResource, it handles "~" like
626:             * {@link #getRequestDispatcher} did.
627:             */
628:            public static final URL getResource(ServletContext ctx, String uri)
629:                    throws MalformedURLException {
630:                return new ParsedURI(ctx, uri).getResource();
631:            }
632:
633:            /** Returns the resource stream of the specified uri.
634:             * Unlike ServletContext.getResource, it handles "~" like
635:             * {@link #getRequestDispatcher} did.
636:             */
637:            public static final InputStream getResourceAsStream(
638:                    ServletContext ctx, String uri) {
639:                return new ParsedURI(ctx, uri).getResourceAsStream();
640:            }
641:
642:            /** Used to resolve "~" in URI. */
643:            private static class ParsedURI {
644:                private ServletContext _svlctx;
645:                private ExtendletContext _extctx;
646:                private String _uri;
647:
648:                private ParsedURI(final ServletContext ctx, final String uri) {
649:                    if (uri != null && uri.startsWith("~")) { //refer to foreign context
650:                        final int j = uri.indexOf('/', 1);
651:                        final String ctxroot;
652:                        if (j >= 0) {
653:                            ctxroot = "/" + uri.substring(1, j);
654:                            _uri = uri.substring(j);
655:                        } else {
656:                            ctxroot = "/" + uri.substring(1);
657:                            _uri = "/";
658:                        }
659:
660:                        _extctx = getExtendletContext(ctx, ctxroot.substring(1));
661:                        if (_extctx == null) {
662:                            _svlctx = ctx.getContext(ctxroot);
663:                            if (_svlctx == null)
664:                                throw new SystemException(
665:                                        "Context not found or not visible to "
666:                                                + ctx + ": " + ctxroot);
667:                        }
668:                    } else {
669:                        _svlctx = ctx;
670:                        _uri = uri;
671:                    }
672:                }
673:
674:                private RequestDispatcher getRequestDispatcher(Map params,
675:                        int mode) {
676:                    if (_extctx == null && _svlctx == null) //not found
677:                        return null;
678:
679:                    final String uri = generateURI(_uri, params, mode);
680:                    return _svlctx != null ? _svlctx.getRequestDispatcher(uri)
681:                            : _extctx.getRequestDispatcher(uri);
682:                }
683:
684:                private URL getResource() throws MalformedURLException {
685:                    return _svlctx != null ? _svlctx.getResource(_uri)
686:                            : _extctx != null ? _extctx.getResource(_uri)
687:                                    : null;
688:                }
689:
690:                private InputStream getResourceAsStream() {
691:                    return _svlctx != null ? _svlctx.getResourceAsStream(_uri)
692:                            : _extctx != null ? _extctx
693:                                    .getResourceAsStream(_uri) : null;
694:                }
695:            }
696:
697:            /** Whether to overwrite uri if both uri and params contain the same
698:             * parameter.
699:             * Used by {@link #generateURI}
700:             */
701:            public static final int OVERWRITE_URI = 0;
702:            /** Whether to ignore params if both uri and params contain the same
703:             * parameter.
704:             * Used by {@link #generateURI}
705:             */
706:            public static final int IGNORE_PARAM = 1;
707:            /** Whether to append params if both uri and params contain the same
708:             * parameter. In other words, they both appear as the final query string.
709:             * Used by {@link #generateURI}
710:             */
711:            public static final int APPEND_PARAM = 2;
712:            /** Whether the specified parameters shall be passed thru the request
713:             * attribute called arg.
714:             */
715:            public static final int PASS_THRU_ATTR = 3;
716:
717:            /** Generates URI by appending the parameters.
718:             * Note: it doesn't support the ~xxx/ format.
719:             *
720:             * @param params the parameters to apend to the query string
721:             * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM},
722:             * {@link #APPEND_PARAM} and {@link #PASS_THRU_ATTR}.
723:             * It defines how to handle if both uri and params contains the same
724:             * parameter.
725:             * mode is used only if both uri contains query string and params is
726:             * not empty.
727:             * @see Encodes#encodeURL(ServletContext, ServletRequest, ServletResponse, String)
728:             */
729:            public static final String generateURI(String uri, Map params,
730:                    int mode) {
731:                if (uri.startsWith("~"))
732:                    throw new IllegalArgumentException(
733:                            "~ctx not supported here: " + uri);
734:
735:                final int j = uri.indexOf('?');
736:                String qstr = null;
737:                if (j >= 0) {
738:                    qstr = uri.substring(j);
739:                    uri = uri.substring(0, j);
740:                }
741:
742:                //if (D.ON && uri.indexOf('%') >= 0)
743:                //	log.warning(new IllegalStateException("You might encode URL twice: "+uri));
744:                //might too annoying
745:
746:                try {
747:                    uri = Encodes.encodeURI(uri);
748:                    final boolean noQstr = qstr == null;
749:                    final boolean noParams = mode == PASS_THRU_ATTR
750:                            || params == null || params.isEmpty();
751:                    if (noQstr && noParams)
752:                        return uri;
753:
754:                    if (noQstr != noParams)
755:                        mode = APPEND_PARAM;
756:
757:                    final StringBuffer sb = new StringBuffer(80).append(uri);
758:                    if (qstr != null)
759:                        sb.append(qstr);
760:
761:                    switch (mode) {
762:                    case IGNORE_PARAM:
763:                        //removing params that is conflict
764:                        for (final Iterator it = params.entrySet().iterator(); it
765:                                .hasNext();) {
766:                            final Map.Entry me = (Map.Entry) it.next();
767:                            final String nm = (String) me.getKey();
768:                            if (Encodes.containsQuery(qstr, nm))
769:                                it.remove();
770:                        }
771:                        //flow thru
772:                    case OVERWRITE_URI:
773:                        return Encodes.setToQueryString(sb, params).toString();
774:                    case APPEND_PARAM:
775:                        return Encodes.addToQueryString(sb, params).toString();
776:                    default:
777:                        throw new IllegalArgumentException("Unknown mode: "
778:                                + mode);
779:                    }
780:                } catch (UnsupportedEncodingException ex) {
781:                    throw new SystemException(ex);
782:                }
783:            }
784:
785:            /** A list of context root paths (e.g., "/abc"). */
786:            private static List _ctxroots;
787:
788:            /** Returns a list of context paths (e.g., "/abc") that this application
789:             * has. This implementation parse application.xml. For war that doesn't
790:             * contain application.xml might have to override this method and
791:             * parse another file to know what context being loaded.
792:             */
793:            public static final List getContextPaths() {
794:                if (_ctxroots != null)
795:                    return _ctxroots;
796:
797:                try {
798:                    synchronized (Servlets.class) {
799:                        return _ctxroots = myGetContextPaths();
800:                    }
801:                } catch (Exception ex) {
802:                    throw SystemException.Aide.wrap(ex);
803:                }
804:            }
805:
806:            private static final List myGetContextPaths() throws Exception {
807:                final String APP_XML = "/META-INF/application.xml";
808:                final List ctxroots = new LinkedList();
809:                final URL xmlURL = Locators.getDefault().getResource(APP_XML);
810:                if (xmlURL == null)
811:                    throw new SystemException("File not found: " + APP_XML);
812:
813:                //		if (log.debugable()) log.debug("Parsing "+APP_XML);
814:                final Element root = new SAXBuilder(false, false, true).build(
815:                        xmlURL).getRootElement();
816:
817:                for (Iterator it = root.getElements("module").iterator(); it
818:                        .hasNext();) {
819:                    final Element e = (Element) it.next();
820:                    final String ctxroot = (String) e
821:                            .getContent("web/context-root");
822:                    if (ctxroot == null) {
823:                        //				if (D.ON && log.finerable()) log.finer("Skip non-web: "+e.getContent("java"));
824:                        continue;
825:                    }
826:
827:                    ctxroots.add(ctxroot.startsWith("/") ? ctxroot : "/"
828:                            + ctxroot);
829:                }
830:
831:                //		log.info("Context found: "+ctxroots);
832:                return new ArrayList(ctxroots);
833:            }
834:
835:            /** Returns a token to represent a limit-time offer.
836:             * It is mainly used as an parameter value (mostlycalled zk_lto), and then
837:             * you could verify whether it is expired by {@link #isOfferExpired}.
838:             */
839:            public static final String getLimitTimeOffer() {
840:                final String lto = Long.toHexString(System.currentTimeMillis());
841:                return lto + Checksums.getChecksum(lto, "");
842:            }
843:
844:            /** Returns whether a token returned by getLimitTimeOffer expired.
845:             * @param timeout how long the office shall expire, unit: seconds.
846:             */
847:            public static final boolean isOfferExpired(String lto, int timeout) {
848:                final int len = lto != null ? lto.length() : 0;
849:                if (len <= 1)
850:                    return true;
851:
852:                final char cksm = lto.charAt(len - 1);
853:                lto = lto.substring(0, len - 1);
854:                if (cksm != Checksums.getChecksum(lto, ""))
855:                    return true;
856:
857:                try {
858:                    return Long.parseLong(lto, 16) + timeout * 1000L < System
859:                            .currentTimeMillis();
860:                } catch (NumberFormatException ex) {
861:                    return true;
862:                }
863:            }
864:
865:            /** Adds an extended context.
866:             * @return the previous extended context, if any, associated with
867:             * the specified name.
868:             */
869:            public static final ExtendletContext addExtendletContext(
870:                    ServletContext ctx, String name, ExtendletContext extctx) {
871:                if (name == null || extctx == null)
872:                    throw new IllegalArgumentException("null");
873:                return (ExtendletContext) getExtWebCtxs(ctx).put(name, extctx);
874:            }
875:
876:            /** Removes an extended context of the specified name.
877:             */
878:            public static final ExtendletContext removeExtendletContext(
879:                    ServletContext ctx, String name) {
880:                return (ExtendletContext) getExtWebCtxs(ctx).remove(name);
881:            }
882:
883:            /** Returns the extended context of the specified name.
884:             */
885:            public static final ExtendletContext getExtendletContext(
886:                    ServletContext ctx, String name) {
887:                return (ExtendletContext) getExtWebCtxs(ctx).get(name);
888:            }
889:
890:            private static final Map getExtWebCtxs(ServletContext ctx) {
891:                synchronized (Servlets.class) { //don't use ctx because it might be a proxy (in portlet)
892:                    final String attr = "javax.zkoss.web.servlets.ExtendletContexts";
893:                    //such that it could be shared among portlets
894:                    Map ctxs = (Map) ctx.getAttribute(attr);
895:                    if (ctxs == null)
896:                        ctx.setAttribute(attr, ctxs = Collections
897:                                .synchronizedMap(new HashMap(5)));
898:                    return ctxs;
899:                }
900:            }
901:
902:            /** Returns the file extension of the specified path, or null
903:             * if no extension at all.
904:             *
905:             * <p>Note: the extension is converted to the lower case.
906:             *
907:             * <p>Note: it assumes the session ID, if any, starts with semicolon.
908:             * For example, the path could be "/a/b.zul;jsession=xxx".
909:             *
910:             * @param path the path. If path is null, null is returned.
911:             * @since 2.4.1
912:             */
913:            public static final String getExtension(String path) {
914:                if (path != null) {
915:                    int j = path.lastIndexOf('.');
916:                    if (j >= 0 && path.indexOf('/', j + 1) < 0) {
917:                        final String ext = path.substring(j + 1);
918:                        j = ext.indexOf(';');
919:                        return j >= 0 ? ext.substring(0, j).toLowerCase() : ext
920:                                .toLowerCase();
921:                    }
922:                }
923:                return null;
924:            }
925:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.