📄 servlets.java
字号:
//Bug or limitation of Catalina: not accepting HttpServletRequest //othere than the original one or wrapper of original one // //Real Cause: org.apache.catalina.core.ApplicationDispatcher //call unwrapRequest() twice, and then causes ClassCastException // //Resolution: since it is the almost last statement, it is safe //to ignore this exception if (!(request instanceof org.zkoss.web.portlet.RenderHttpServletRequest)) throw ex; //not the case described above } finally { restorePassThruAttr(request, old); } } else { disp.forward(request, response); } } /** A shortcut of forward(request, response, uri, null, 0). */ public static final void forward(ServletContext ctx, ServletRequest request, ServletResponse response, String uri) throws IOException, ServletException { forward(ctx, request, response, uri, null, 0); } /** * Includes the resource at the specified URI. * It enhances RequestDispatcher to allow the inclusion with * a parameter map -- acutually converting parameters to a query string * and appending it to uri. * * <p>NOTE: don't include query parameters in uri. * * @param ctx the servlet context. If null, uri cannot be foreign URI. * It is ignored if URI is relevant (neither starts with '/' nor '~'). * @param uri the URI to include. It is OK to relevant (without leading * '/'). If starts with "/", the context path of request is assumed. * To reference to foreign context, use "~ctx/" where ctx is the * context path of the foreign context (without leading '/'). * @param params the parameter map; null to ignore * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM}, * and {@link #APPEND_PARAM}. It defines how to handle if both uri * and params contains the same parameter. */ public static final void include(ServletContext ctx, ServletRequest request, ServletResponse response, String uri, Map params, int mode) throws IOException, ServletException {// if (D.ON && log.debugable()) log.debug("Including "+uri+" at "+ctx); //20050606: Tom Yeh //We have to set this special attribute for jetty //Otherwise, if including a page crossing context might not return //the same session request.setAttribute("org.mortbay.jetty.servlet.Dispatcher.shared_session", Boolean.TRUE); final RequestDispatcher disp = getRequestDispatcher(ctx, request, uri, params, mode); if (disp == null) throw new ServletException("No dispatcher available to include "+uri); if (mode == PASS_THRU_ATTR && params != null && !params.isEmpty()) { final Map old = setPassThruAttr(request, params); try { disp.include(request, response); } finally { restorePassThruAttr(request, old); } } else { disp.include(request, response); } } /** A shortcut of include(request, response, uri, null, 0). */ public static final void include(ServletContext ctx, ServletRequest request, ServletResponse response, String uri) throws IOException, ServletException { include(ctx, request, response, uri, null, 0); } /** Sets the arg attribute to pass parameters thru request's attribute. */ private static final Map setPassThruAttr(ServletRequest request, Map params) { final Map old = (Map)request.getAttribute(Attributes.ARG); request.setAttribute(Attributes.ARG, params); return old; } /** Restores what has been done by {@link #setPassThruAttr}. */ private static final void restorePassThruAttr(ServletRequest request, Map old) { if (old != null) request.setAttribute(Attributes.ARG, old); else request.removeAttribute(Attributes.ARG); } /** Returns the request dispatch of the specified URI. * * @param ctx the servlet context. If null, uri cannot be foreign URI. * It is ignored if uri is relevant (neither starts with '/' nor '~'). * @param request the request. If null, uri cannot be relevant. * It is used only if uri is relevant. * @param uri the URI to include. It is OK to relevant (without leading * '/'). If starts with "/", the context path of request is assumed. * To reference to foreign context, use "~ctx/" where ctx is the * context path of the foreign context (without leading '/'). * @param params the parameter map; null to ignore * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM}, * and {@link #APPEND_PARAM}. It defines how to handle if both uri * and params contains the same parameter. */ public static final RequestDispatcher getRequestDispatcher(ServletContext ctx, ServletRequest request, String uri, Map params, int mode) throws ServletException { final char cc = uri.length() > 0 ? uri.charAt(0): (char)0; if (ctx == null || (cc != '/' && cc != '~')) {//... or relevant if (request == null) throw new IllegalArgumentException( ctx == null ? "Servlet context and request cannot be both null": "Request is required to use revalant URI: "+uri); if (cc == '~') throw new IllegalArgumentException("Servlet context is required to use foreign URI: "+uri); uri = generateURI(uri, params, mode); return request.getRequestDispatcher(uri); } //NO NEED to encodeURL since it is forward/include return new ParsedURI(ctx, uri).getRequestDispatcher(params, mode); } /** Returns the resource of the specified uri. * Unlike ServletContext.getResource, it handles "~" like * {@link #getRequestDispatcher} did. */ public static final URL getResource(ServletContext ctx, String uri) throws MalformedURLException { return new ParsedURI(ctx, uri).getResource(); } /** Returns the resource stream of the specified uri. * Unlike ServletContext.getResource, it handles "~" like * {@link #getRequestDispatcher} did. */ public static final InputStream getResourceAsStream( ServletContext ctx, String uri) { return new ParsedURI(ctx, uri).getResourceAsStream(); } /** Used to resolve "~" in URI. */ private static class ParsedURI { private ServletContext _svlctx; private ExtendletContext _extctx; private String _uri; private ParsedURI(final ServletContext ctx, final String uri) { if (uri != null && uri.startsWith("~")) { //refer to foreign context final int j = uri.indexOf('/', 1); final String ctxroot; if (j >= 0) { ctxroot = "/" + uri.substring(1, j); _uri = uri.substring(j); } else { ctxroot = "/" + uri.substring(1); _uri = "/"; } _extctx = getExtendletContext(ctx, ctxroot.substring(1)); if (_extctx == null) { _svlctx = ctx.getContext(ctxroot); if (_svlctx == null) throw new SystemException("Context not found or not visible to "+ctx+": "+ctxroot); } } else { _svlctx = ctx; _uri = uri; } } private RequestDispatcher getRequestDispatcher(Map params, int mode) { if (_extctx == null && _svlctx == null) //not found return null; final String uri = generateURI(_uri, params, mode); return _svlctx != null ? _svlctx.getRequestDispatcher(uri): _extctx.getRequestDispatcher(uri); } private URL getResource() throws MalformedURLException { return _svlctx != null ? _svlctx.getResource(_uri): _extctx != null ? _extctx.getResource(_uri): null; } private InputStream getResourceAsStream() { return _svlctx != null ? _svlctx.getResourceAsStream(_uri): _extctx != null ? _extctx.getResourceAsStream(_uri): null; } } /** Whether to overwrite uri if both uri and params contain the same * parameter. * Used by {@link #generateURI} */ public static final int OVERWRITE_URI = 0; /** Whether to ignore params if both uri and params contain the same * parameter. * Used by {@link #generateURI} */ public static final int IGNORE_PARAM = 1; /** Whether to append params if both uri and params contain the same * parameter. In other words, they both appear as the final query string. * Used by {@link #generateURI} */ public static final int APPEND_PARAM = 2; /** Whether the specified parameters shall be passed thru the request * attribute called arg. */ public static final int PASS_THRU_ATTR = 3; /** Generates URI by appending the parameters. * Note: it doesn't support the ~xxx/ format. * * @param params the parameters to apend to the query string * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM}, * {@link #APPEND_PARAM} and {@link #PASS_THRU_ATTR}. * It defines how to handle if both uri and params contains the same * parameter. * mode is used only if both uri contains query string and params is * not empty. * @see Encodes#encodeURL(ServletContext, ServletRequest, ServletResponse, String) */ public static final String generateURI(String uri, Map params, int mode) { if (uri.startsWith("~")) throw new IllegalArgumentException("~ctx not supported here: "+uri); final int j = uri.indexOf('?'); String qstr = null; if (j >= 0) { qstr = uri.substring(j); uri = uri.substring(0, j); } //if (D.ON && uri.indexOf('%') >= 0) // log.warning(new IllegalStateException("You might encode URL twice: "+uri)); //might too annoying try { uri = Encodes.encodeURI(uri); final boolean noQstr = qstr == null; final boolean noParams = mode == PASS_THRU_ATTR || params == null || params.isEmpty(); if (noQstr && noParams) return uri; if (noQstr != noParams) mode = APPEND_PARAM; final StringBuffer sb = new StringBuffer(80).append(uri); if (qstr != null) sb.append(qstr); switch (mode) { case IGNORE_PARAM: //removing params that is conflict for (final Iterator it = params.entrySet().iterator(); it.hasNext();) { final Map.Entry me = (Map.Entry)it.next(); final String nm = (String)me.getKey(); if (Encodes.containsQuery(qstr, nm)) it.remove(); } //flow thru case OVERWRITE_URI: return Encodes.setToQueryString(sb, params).toString(); case APPEND_PARAM: return Encodes.addToQueryString(sb, params).toString(); default: throw new IllegalArgumentException("Unknown mode: "+mode); } } catch (UnsupportedEncodingException ex) { throw new SystemException(ex); } } /** A list of context root paths (e.g., "/abc"). */ private static List _ctxroots; /** Returns a list of context paths (e.g., "/abc") that this application * has. This implementation parse application.xml. For war that doesn't * contain application.xml might have to override this method and * parse another file to know what context being loaded. */ public static final List getContextPaths() { if (_ctxroots != null) return _ctxroots; try { synchronized (Servlets.class) { return _ctxroots = myGetContextPaths(); } } catch (Exception ex) { throw SystemException.Aide.wrap(ex); } } private static final List myGetContextPaths() throws Exception { final String APP_XML = "/META-INF/application.xml"; final List ctxroots = new LinkedList(); final URL xmlURL = Locators.getDefault().getResource(APP_XML); if (xmlURL == null) throw new SystemException("File not found: "+APP_XML);// if (log.debugable()) log.debug("Parsing "+APP_XML); final Element root = new SAXBuilder(false,false,true).build(xmlURL).getRootElement(); for (Iterator it = root.getElements("module").iterator(); it.hasNext();) { final Element e = (Element)it.next(); final String ctxroot = (String)e.getContent("web/context-root"); if (ctxroot == null) {// if (D.ON && log.finerable()) log.finer("Skip non-web: "+e.getContent("java")); continue; } ctxroots.add(ctxroot.startsWith("/") ? ctxroot: "/" + ctxroot); }// log.info("Context found: "+ctxroots); return new ArrayList(ctxroots); } /** Returns a token to represent a limit-time offer. * It is mainly used as an parameter value (mostlycalled zk_lto), and then * you could verify whether it is expired by {@link #isOfferExpired}. */ public static final String getLimitTimeOffer() { final String lto = Long.toHexString(System.currentTimeMillis()); return lto + Checksums.getChecksum(lto, ""); } /** Returns whether a token returned by getLimitTimeOffer expired. * @param timeout how long the office shall expire, unit: seconds. */ public static final boolean isOfferExpired(String lto, int timeout) { final int len = lto != null ? lto.length(): 0; if (len <= 1) return true; final char cksm = lto.charAt(len - 1); lto = lto.substring(0, len - 1); if (cksm != Checksums.getChecksum(lto, "")) return true; try { return Long.parseLong(lto, 16) + timeout*1000L < System.currentTimeMillis(); } catch (NumberFormatException ex) { return true; } } /** Adds an extended context. * @return the previous extended context, if any, associated with * the specified name. */ public static final ExtendletContext addExtendletContext(ServletContext ctx, String name, ExtendletContext extctx) { if (name == null || extctx == null) throw new IllegalArgumentException("null"); return (ExtendletContext)getExtWebCtxs(ctx).put(name, extctx); } /** Removes an extended context of the specified name. */ public static final ExtendletContext removeExtendletContext(ServletContext ctx, String name) { return (ExtendletContext)getExtWebCtxs(ctx).remove(name); } /** Returns the extended context of the specified name. */ public static final ExtendletContext getExtendletContext(ServletContext ctx, String name) { return (ExtendletContext)getExtWebCtxs(ctx).get(name); } private static final Map getExtWebCtxs(ServletContext ctx) { synchronized (Servlets.class) { //don't use ctx because it might be a proxy (in portlet) final String attr = "javax.zkoss.web.servlets.ExtendletContexts"; //such that it could be shared among portlets Map ctxs = (Map)ctx.getAttribute(attr); if (ctxs == null) ctx.setAttribute(attr, ctxs = Collections.synchronizedMap(new HashMap(5))); return ctxs; } } /** Returns the file extension of the specified path, or null * if no extension at all. * * <p>Note: the extension is converted to the lower case. * * <p>Note: it assumes the session ID, if any, starts with semicolon. * For example, the path could be "/a/b.zul;jsession=xxx". * * @param path the path. If path is null, null is returned. * @since 2.4.1 */ public static final String getExtension(String path) { if (path != null) { int j = path.lastIndexOf('.'); if (j >= 0 && path.indexOf('/', j + 1) < 0) { final String ext = path.substring(j + 1); j = ext.indexOf(';'); return j >= 0 ? ext.substring(0, j).toLowerCase(): ext.toLowerCase(); } } return null; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -