Java tutorial
/* * Helma License Notice * * The contents of this file are subject to the Helma License * Version 2.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://adele.helma.org/download/helma/license.txt * * Copyright 1998-2003 Helma Software. All Rights Reserved. * * $RCSfile: AbstractServletClient.java,v $ * $Author: hannes $ * $Revision: 1.68 $ * $Date: 2006/06/03 07:13:06 $ */ /* Portierung von helma.asp.AspClient auf Servlets */ /* Author: Raphael Spannocchi Datum: 27.11.1998 */ /* * Modified by: * * Axiom Software Inc., 11480 Commerce Park Drive, Third Floor, Reston, VA 20191 USA * email: info@axiomsoftwareinc.com */ package axiom.servlet; import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUpload; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.servlet.ServletRequestContext; import org.mozilla.javascript.ClassShutter; import org.mozilla.javascript.Context; import org.mozilla.javascript.ImporterTopLevel; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import axiom.framework.*; import axiom.framework.core.Application; import axiom.util.*; /** * This is an abstract Axiom servlet adapter. This class communicates with axiom applications * via RMI. Subclasses are either one servlet per app, or one servlet that handles multiple apps */ public abstract class AbstractServletClient extends HttpServlet { // host on which Axiom app is running String host = null; // port of Axiom RMI server int port = 0; // RMI url of Axiom app String axiomUrl; // limit to HTTP uploads in kB int uploadLimit = 1024; // limit to HTTP upload int totalUploadLimit = 1024; // cookie domain to use String cookieDomain; // cookie name for session cookies String sessionCookieName = "AxiomSession"; // this tells us whether to bind session cookies to client ip subnets // so they can't be easily used from other ip addresses when hijacked boolean protectedSessionCookie = true; // allow caching of responses boolean caching; // enable debug output boolean debug; // soft fail on file upload errors by setting flag "axiom_upload_error" in RequestTrans // if fals, an error response is written to the client immediately without entering Axiom boolean uploadSoftfail = false; /** * Init this servlet. * * @param init the servlet configuration * * @throws ServletException ... */ public void init(ServletConfig init) throws ServletException { super.init(init); // get max size for file uploads String upstr = init.getInitParameter("uploadLimit"); try { uploadLimit = (upstr == null) ? 1024 : Integer.parseInt(upstr); } catch (NumberFormatException x) { System.err.println("Bad number format for uploadLimit: " + upstr); uploadLimit = 1024; } // get max total upload size upstr = init.getInitParameter("totalUploadLimit"); try { totalUploadLimit = (upstr == null) ? uploadLimit : Integer.parseInt(upstr); } catch (NumberFormatException x) { log("Bad number format for totalUploadLimit: " + upstr); totalUploadLimit = uploadLimit; } // soft fail mode for upload errors uploadSoftfail = ("true".equalsIgnoreCase(init.getInitParameter("uploadSoftfail"))); // get cookie domain cookieDomain = init.getInitParameter("cookieDomain"); if (cookieDomain != null) { cookieDomain = cookieDomain.toLowerCase(); } // get session cookie name sessionCookieName = init.getInitParameter("sessionCookieName"); if (sessionCookieName == null) { sessionCookieName = "AxiomSession"; } // disable binding session cookie to ip address? protectedSessionCookie = !("false".equalsIgnoreCase(init.getInitParameter("protectedSessionCookie"))); // debug mode for printing out detailed error messages debug = ("true".equalsIgnoreCase(init.getInitParameter("debug"))); // generally disable response caching for clients? caching = !("false".equalsIgnoreCase(init.getInitParameter("caching"))); } /** * Abstract method to get the {@link axiom.framework.core.Application Applicaton} * instance the servlet is talking to. * * @return this servlet's application instance */ abstract Application getApplication(); /** * Handle a request. * * @param request ... * @param response ... * * @throws ServletException ... * @throws IOException ... */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final String httpMethod = request.getMethod(); if (!"POST".equalsIgnoreCase(httpMethod) && !"GET".equalsIgnoreCase(httpMethod)) { sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "HTTP Method " + httpMethod + " not supported."); return; } RequestTrans reqtrans = new RequestTrans(request, response, getPathInfo(request)); try { // get the character encoding String encoding = request.getCharacterEncoding(); if (encoding == null) { // no encoding from request, use the application's charset encoding = getApplication().getCharset(); } // read and set http parameters parseParameters(request, reqtrans, encoding); List uploads = null; ServletRequestContext reqcx = new ServletRequestContext(request); if (ServletFileUpload.isMultipartContent(reqcx)) { // get session for upload progress monitoring UploadStatus uploadStatus = getApplication().getUploadStatus(reqtrans); try { uploads = parseUploads(reqcx, reqtrans, uploadStatus, encoding); } catch (Exception upx) { System.err.println("Error in file upload: " + upx); if (uploadSoftfail) { String msg = upx.getMessage(); if (msg == null || msg.length() == 0) { msg = upx.toString(); } reqtrans.set("axiom_upload_error", msg); } else if (upx instanceof FileUploadBase.SizeLimitExceededException) { sendError(response, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, "File upload size exceeds limit of " + uploadLimit + "kB"); return; } else { sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error in file upload: " + upx); return; } } } parseCookies(request, reqtrans, encoding); // do standard HTTP variables String host = request.getHeader("Host"); if (host != null) { host = host.toLowerCase(); reqtrans.set("http_host", host); } String referer = request.getHeader("Referer"); if (referer != null) { reqtrans.set("http_referer", referer); } try { long ifModifiedSince = request.getDateHeader("If-Modified-Since"); if (ifModifiedSince > -1) { reqtrans.setIfModifiedSince(ifModifiedSince); } } catch (IllegalArgumentException ignore) { } String ifNoneMatch = request.getHeader("If-None-Match"); if (ifNoneMatch != null) { reqtrans.setETags(ifNoneMatch); } String remotehost = request.getRemoteAddr(); if (remotehost != null) { reqtrans.set("http_remotehost", remotehost); } // get the cookie domain to use for this response, if any. String resCookieDomain = cookieDomain; if (resCookieDomain != null) { // check if cookieDomain is valid for this response. // (note: cookieDomain is guaranteed to be lower case) // check for x-forwarded-for header, fix for bug 443 String proxiedHost = request.getHeader("x-forwarded-host"); if (proxiedHost != null) { if (proxiedHost.toLowerCase().indexOf(cookieDomain) == -1) { resCookieDomain = null; } } else if ((host != null) && host.toLowerCase().indexOf(cookieDomain) == -1) { resCookieDomain = null; } } // check if session cookie is present and valid, creating it if not. checkSessionCookie(request, response, reqtrans, resCookieDomain); String browser = request.getHeader("User-Agent"); if (browser != null) { reqtrans.set("http_browser", browser); } String language = request.getHeader("Accept-Language"); if (language != null) { reqtrans.set("http_language", language); } String authorization = request.getHeader("authorization"); if (authorization != null) { reqtrans.set("authorization", authorization); } ResponseTrans restrans = getApplication().execute(reqtrans); // if the response was already written and committed by the application // we can skip this part and return if (response.isCommitted()) { return; } // set cookies if (restrans.countCookies() > 0) { CookieTrans[] resCookies = restrans.getCookies(); for (int i = 0; i < resCookies.length; i++) try { Cookie c = resCookies[i].getCookie("/", resCookieDomain); response.addCookie(c); } catch (Exception ignore) { ignore.printStackTrace(); } } // write response writeResponse(request, response, reqtrans, restrans); } catch (Exception x) { try { if (debug) { sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server error: " + x); x.printStackTrace(); } else { sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "The server encountered an error while processing your request. " + "Please check back later."); } log("Exception in execute: " + x); } catch (IOException io_e) { log("Exception in sendError: " + io_e); } } } protected void writeResponse(HttpServletRequest req, HttpServletResponse res, RequestTrans axiomreq, ResponseTrans axiomres) throws IOException { if (axiomres.getForward() != null) { sendForward(res, req, axiomres); return; } if (axiomres.getETag() != null) { res.setHeader("ETag", axiomres.getETag()); } if (axiomres.getRedirect() != null) { sendRedirect(req, res, axiomres.getRedirect()); } else if (axiomres.getNotModified()) { res.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { if (!axiomres.isCacheable() || !caching) { // Disable caching of response. // for HTTP 1.0 res.setDateHeader("Expires", System.currentTimeMillis() - 10000); res.setHeader("Pragma", "no-cache"); // for HTTP 1.1 res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0"); } if (axiomres.getStatus() > 0) { res.setStatus(axiomres.getStatus()); } // set last-modified header to now long modified = axiomres.getLastModified(); if (modified > -1) { res.setDateHeader("Last-Modified", modified); } res.setDateHeader("Date", System.currentTimeMillis()); res.setContentLength(axiomres.getContentLength()); res.setContentType(axiomres.getContentType()); if ("HEAD".equalsIgnoreCase(req.getMethod())) { return; } try { OutputStream out = res.getOutputStream(); InputStream istream = axiomres.getInputStream(); if (istream != null) { try { byte[] buf = new byte[8192]; int len; while ((len = istream.read(buf)) != -1) { out.write(buf, 0, len); } out.flush(); } finally { axiomres.closeInputStream(); } } else { out.write(axiomres.getContent()); out.flush(); } } catch (Exception io_e) { log("Exception in writeResponse: " + io_e); } } } void sendError(HttpServletResponse response, int code, String message) throws IOException { response.reset(); response.setStatus(code); response.setContentType("text/html"); Writer writer = response.getWriter(); writer.write("<html><body><h3>"); writer.write("Error in application "); try { writer.write(getApplication().getName()); } catch (Exception besafe) { besafe.printStackTrace(); } writer.write("</h3>"); writer.write(message); writer.write("</body></html>"); writer.flush(); } void sendRedirect(HttpServletRequest req, HttpServletResponse res, String url) { String location = url; if (url.indexOf("://") == -1) { // need to transform a relative URL into an absolute one String scheme = req.getScheme(); StringBuffer loc = new StringBuffer(scheme); loc.append("://"); String hostname = req.getServerName(); boolean forwarded = false; if (req.getHeader("X-Forwarded-Host") != null) { hostname = req.getHeader("X-Forwarded-Host"); forwarded = true; } loc.append(hostname); int p = (!forwarded) ? req.getServerPort() : 80; // check if we need to include server port if ((p > 0) && (("http".equals(scheme) && (p != 80)) || ("https".equals(scheme) && (p != 443)))) { loc.append(":"); loc.append(p); } if (!url.startsWith("/")) { String requri = req.getRequestURI(); int lastSlash = requri.lastIndexOf("/"); if (lastSlash == (requri.length() - 1)) { loc.append(requri); } else if (lastSlash > -1) { loc.append(requri.substring(0, lastSlash + 1)); } else { loc.append("/"); } } loc.append(url); location = loc.toString(); } // send status code 303 for HTTP 1.1, 302 otherwise if (isOneDotOne(req.getProtocol())) { res.setStatus(HttpServletResponse.SC_SEE_OTHER); } else { res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); } res.setContentType("text/html"); res.setHeader("Location", location); } /** * Forward the request to a static file. The file must be reachable via * the context's protectedStatic resource base. */ void sendForward(HttpServletResponse res, HttpServletRequest req, ResponseTrans axiomres) throws IOException { String forward = axiomres.getForward(); ServletContext cx = getServletConfig().getServletContext(); String path = cx.getRealPath(forward); if (path == null) throw new IOException("Resource " + forward + " not found"); File file = new File(path); // calculate checksom on last modified date and content length. byte[] checksum = getChecksum(file); String etag = "\"" + new String(Base64.encode(checksum)) + "\""; res.setHeader("ETag", etag); String etagHeader = req.getHeader("If-None-Match"); if (etagHeader != null) { StringTokenizer st = new StringTokenizer(etagHeader, ", \r\n"); while (st.hasMoreTokens()) { if (etag.equals(st.nextToken())) { res.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } } } int length = (int) file.length(); res.setContentLength(length); res.setContentType(axiomres.getContentType()); InputStream in = cx.getResourceAsStream(forward); if (in == null) throw new IOException("Can't read " + path); try { OutputStream out = res.getOutputStream(); int bufferSize = 4096; byte buffer[] = new byte[bufferSize]; int l; while (length > 0) { if (length < bufferSize) l = in.read(buffer, 0, length); else l = in.read(buffer, 0, bufferSize); if (l == -1) break; length -= l; out.write(buffer, 0, l); } } finally { in.close(); } } private byte[] getChecksum(File file) { byte[] checksum = new byte[16]; long n = file.lastModified(); for (int i = 0; i < 8; i++) { checksum[i] = (byte) (n); n >>>= 8; } n = file.length(); for (int i = 8; i < 16; i++) { checksum[i] = (byte) (n); n >>>= 8; } return checksum; } /** * Used to build the form value array when a multipart (file upload) form has * multiple values for one form element name. * * @param reqtrans * @param name * @param value */ private void appendFormValue(RequestTrans reqtrans, String name, Object value) { String arrayName = name; try { Context cx = Context.enter(); Object curr = reqtrans.get(arrayName); if (curr instanceof NativeArray) { NativeArray na = (NativeArray) curr; na.put((int) na.getLength(), na, value); } else { Object[] values = new Object[2]; values[0] = curr; values[1] = value; NativeArray na = new NativeArray(values); ImporterTopLevel scope = new ImporterTopLevel(cx, true); ScriptRuntime.setObjectProtoAndParent(na, scope); reqtrans.set(arrayName, na); } } catch (ClassCastException x) { // name_array is defined as something else in the form - don't overwrite it } finally { Context.exit(); } } /** * Check if the session cookie is set and valid for this request. * If not, create a new one. */ private void checkSessionCookie(HttpServletRequest request, HttpServletResponse response, RequestTrans reqtrans, String domain) { // check if we need to create a session id. if (protectedSessionCookie) { // If protected session cookies are enabled we also force a new session // if the existing session id doesn't match the client's ip address StringBuffer b = new StringBuffer(); if (reqtrans.getSession() == null || !reqtrans.getSession().startsWith(b.toString())) { response.addCookie(createSessionCookie(b, reqtrans, domain)); } } else if (reqtrans.getSession() == null) { response.addCookie(createSessionCookie(new StringBuffer(), reqtrans, domain)); } } /** * Create a new session cookie. * * @param b * @param reqtrans * @param domain * @return the session cookie */ private Cookie createSessionCookie(StringBuffer b, RequestTrans reqtrans, String domain) { b.append(Long.toString(Math.round(Math.random() * Long.MAX_VALUE) - System.currentTimeMillis(), 36)); reqtrans.setSession(b.toString()); Cookie cookie = new Cookie(sessionCookieName, reqtrans.getSession()); cookie.setPath("/"); if (domain != null) { cookie.setDomain(domain); } return cookie; } /** * Adds an the 3 most significant bytes of an IP address header to the * session cookie id. Some headers may contain a list of IP addresses * separated by comma - in that case, care is taken that only the first * one is considered. */ private void addIPAddress(StringBuffer b, String addr) { if (addr != null) { int cut = addr.indexOf(','); if (cut > -1) { addr = addr.substring(0, cut); } cut = addr.lastIndexOf('.'); if (cut == -1) { cut = addr.lastIndexOf(':'); } if (cut > -1) { b.append(addr.substring(0, cut + 1)); } } } /** * Put name and value pair in map. When name already exist, add value * to array of values. */ private static void putMapEntry(Map map, String name, String value) { String[] newValues = null; String[] oldValues = (String[]) map.get(name); if (oldValues == null) { newValues = new String[1]; newValues[0] = value; } else { newValues = new String[oldValues.length + 1]; System.arraycopy(oldValues, 0, newValues, 0, oldValues.length); newValues[oldValues.length] = value; } map.put(name, newValues); } protected List parseUploads(ServletRequestContext reqcx, RequestTrans reqtrans, final UploadStatus uploadStatus, String encoding) throws FileUploadException, UnsupportedEncodingException { List uploads = null; Context cx = Context.enter(); ImporterTopLevel scope = new ImporterTopLevel(cx, true); try { // handle file upload DiskFileItemFactory factory = new DiskFileItemFactory(); FileUpload upload = new FileUpload(factory); // use upload limit for individual file size, but also set a limit on overall size upload.setFileSizeMax(uploadLimit * 1024); upload.setSizeMax(totalUploadLimit * 1024); // register upload tracker with user's session if (uploadStatus != null) { upload.setProgressListener(new ProgressListener() { public void update(long bytesRead, long contentLength, int itemsRead) { uploadStatus.update(bytesRead, contentLength, itemsRead); } }); } uploads = upload.parseRequest(reqcx); Iterator it = uploads.iterator(); while (it.hasNext()) { FileItem item = (FileItem) it.next(); String name = item.getFieldName(); Object value = null; // check if this is an ordinary HTML form element or a file upload if (item.isFormField()) { value = item.getString(encoding); } else { String itemName = item.getName().trim(); if (itemName == null || itemName.equals("")) { continue; } value = new MimePart(itemName, item.get(), item.getContentType()); value = new NativeJavaObject(scope, value, value.getClass()); } item.delete(); // if multiple values exist for this name, append to _array // reqtrans.addPostParam(name, value); ???? Object ret = reqtrans.get(name); if (ret != null && ret != Scriptable.NOT_FOUND) { appendFormValue(reqtrans, name, value); } else { reqtrans.set(name, value); } } } finally { Context.exit(); } return uploads; } protected void parseParameters(HttpServletRequest request, RequestTrans reqtrans, String encoding) throws Exception { HashMap parameters = new HashMap(); try { Context cx = Context.enter(); cx.setClassShutter(new ClassShutter() { public boolean visibleToScripts(String fullClassName) { return false; } }); ImporterTopLevel scope = new ImporterTopLevel(cx, true); // Parse any posted parameters in the input stream String contentType; boolean isPost = false, isForm = false, isJson = false, isXml = false; if ("POST".equals(request.getMethod())) { isPost = true; } if (isPost && (contentType = request.getContentType()) != null) { contentType = contentType.split(";")[0]; if ("application/x-www-form-urlencoded".equals(contentType)) { isForm = true; } else if ("text/json".equals(contentType)) { isJson = true; } else if ("text/xml".equals(contentType)) { isXml = true; } } // Parse any query string parameters from the request String queryString = request.getQueryString(); if (queryString != null) { try { parseParameters(parameters, queryString.getBytes(), encoding, isPost); Scriptable sqparam = cx.newObject(scope); for (Iterator i = parameters.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); String key = (String) entry.getKey(); String[] values = (String[]) entry.getValue(); if ((values != null) && (values.length > 0)) { if (values.length == 1) { sqparam.put(key, sqparam, values[0]); } else { NativeArray na = new NativeArray(values); ScriptRuntime.setObjectProtoAndParent(na, scope); sqparam.put(key, sqparam, na); } } } reqtrans.setQueryParams(sqparam); } catch (Exception e) { System.err.println("Error parsing query string: " + e); } } if (isForm || isJson || isXml) { try { int max = request.getContentLength(); int len = 0; byte[] buf = new byte[max]; ServletInputStream is = request.getInputStream(); while (len < max) { int next = is.read(buf, len, max - len); if (next < 0) { break; } len += next; } if (isForm) { HashMap formMap = new HashMap(); parseParameters(formMap, buf, encoding, isPost); Scriptable spparam = cx.newObject(scope); for (Iterator i = formMap.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); String key = (String) entry.getKey(); String[] values = (String[]) entry.getValue(); if (values.length > 0) { if (values.length == 1) { spparam.put(key, spparam, values[0]); } else { NativeArray na = new NativeArray(values); ScriptRuntime.setObjectProtoAndParent(na, scope); spparam.put(key, spparam, na); } } } reqtrans.setPostBody(new String(buf, encoding)); reqtrans.setPostParams(spparam); parameters.putAll(formMap); } else if (isJson) { String json = new String(buf, encoding); Scriptable post = (Scriptable) cx.evaluateString(scope, "eval(" + json + ")", "", 0, null); reqtrans.setPostBody(post); Object[] ids = post.getIds(); int idslen = ids.length; for (int i = 0; i < idslen; i++) { parameters.put(ids[i], post.get((String) ids[i], post)); } } else if (isXml) { String xml = new String(buf, encoding); int startProlog = xml.indexOf("<?xml version="), endProlog; if (startProlog > -1 && (endProlog = xml.indexOf(">", startProlog + 1)) > -1) { xml = new StringBuffer(xml).replace(startProlog, endProlog + 1, "").toString(); } xml = xml.replaceAll("\\\"", "\\\\\"").replaceAll("\n", "").replaceAll("\r", ""); Scriptable post = (Scriptable) cx.evaluateString(scope, "new XML(\"" + xml + "\");", "", 0, null); reqtrans.setPostBody(post); } } catch (Exception e) { throw e; } } Scriptable sreqdata = cx.newObject(scope); for (Iterator i = parameters.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); String key = (String) entry.getKey(); Object val = entry.getValue(); if (val != null) { if (val instanceof String[]) { String[] values = (String[]) val; if (values.length > 0) { if (values.length == 1) { sreqdata.put(key, sreqdata, values[0]); } else { NativeArray na = new NativeArray(values); ScriptRuntime.setObjectProtoAndParent(na, scope); sreqdata.put(key, sreqdata, na); } } } else { sreqdata.put(key, sreqdata, val); } } } reqtrans.setData(sreqdata); } catch (Exception ex) { ex.printStackTrace(); throw ex; } finally { Context.exit(); } } protected void parseCookies(HttpServletRequest request, RequestTrans reqtrans, String encoding) throws Exception { try { Context cx = Context.enter(); cx.setClassShutter(new ClassShutter() { public boolean visibleToScripts(String fullClassName) { return false; } }); ImporterTopLevel scope = new ImporterTopLevel(cx, true); // read cookies Cookie[] reqCookies = request.getCookies(); Scriptable cookies = cx.newObject(scope); if (reqCookies != null) { for (int i = 0; i < reqCookies.length; i++) { try { // get Cookies String nextKey = reqCookies[i].getName(); String nextPart = reqCookies[i].getValue(); if (sessionCookieName.equals(nextKey)) { reqtrans.setSession(nextPart); } else { cookies.put(nextKey, cookies, nextPart); } } catch (Exception badCookie) { // ignore } } } reqtrans.setCookies(cookies); } catch (Exception ex) { ex.printStackTrace(); throw ex; } finally { Context.exit(); } } /** * Append request parameters from the specified String to the specified * Map. It is presumed that the specified Map is not accessed from any * other thread, so no synchronization is performed. * <p> * <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed * individually on the parsed name and value elements, rather than on * the entire query string ahead of time, to properly deal with the case * where the name or value includes an encoded "=" or "&" character * that would otherwise be interpreted as a delimiter. * * NOTE: byte array data is modified by this method. Caller beware. * * @param map Map that accumulates the resulting parameters * @param data Input string containing request parameters * @param encoding Encoding to use for converting hex * * @exception UnsupportedEncodingException if the data is malformed */ public static void parseParameters(Map map, byte[] data, String encoding, boolean isPost) throws UnsupportedEncodingException { if ((data != null) && (data.length > 0)) { int ix = 0; int ox = 0; String key = null; String value = null; while (ix < data.length) { byte c = data[ix++]; switch ((char) c) { case '&': value = new String(data, 0, ox, encoding); if (key != null) { putMapEntry(map, key, value); key = null; } ox = 0; break; case '=': key = new String(data, 0, ox, encoding); ox = 0; break; case '+': data[ox++] = (byte) ' '; break; case '%': data[ox++] = (byte) ((convertHexDigit(data[ix++]) << 4) + convertHexDigit(data[ix++])); break; default: data[ox++] = c; } } if (key != null) { //The last value does not end in '&'. So save it now. value = new String(data, 0, ox, encoding); putMapEntry(map, key, value); } else if (ox > 0) { // Store any residual bytes in req.data.http_post_remainder value = new String(data, 0, ox, encoding); if (isPost) { putMapEntry(map, "http_post_remainder", value); } else { putMapEntry(map, "http_get_remainder", value); } } } } /** * Convert a byte character value to hexidecimal digit value. * * @param b the character value byte */ private static byte convertHexDigit(byte b) { if ((b >= '0') && (b <= '9')) { return (byte) (b - '0'); } if ((b >= 'a') && (b <= 'f')) { return (byte) (b - 'a' + 10); } if ((b >= 'A') && (b <= 'F')) { return (byte) (b - 'A' + 10); } return 0; } boolean isOneDotOne(String protocol) { if (protocol == null) { return false; } if (protocol.endsWith("1.1")) { return true; } return false; } String getPathInfo(HttpServletRequest req) throws UnsupportedEncodingException { StringTokenizer t = new StringTokenizer(req.getContextPath(), "/"); int prefixTokens = t.countTokens(); t = new StringTokenizer(req.getServletPath(), "/"); prefixTokens += t.countTokens(); String uri = req.getRequestURI(); t = new StringTokenizer(uri, "/"); int uriTokens = t.countTokens(); StringBuffer pathbuffer = new StringBuffer(); String encoding = getApplication().getCharset(); for (int i = 0; i < uriTokens; i++) { String token = t.nextToken(); if (i < prefixTokens) { continue; } if (i > prefixTokens) { pathbuffer.append('/'); } pathbuffer.append(UrlEncoded.decode(token, encoding)); } // append trailing "/" if it is contained in original URI if (uri.endsWith("/")) pathbuffer.append('/'); return pathbuffer.toString(); } /** * * * @return ... */ public String getServletInfo() { return new String("Axiom Servlet Client"); } }