/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2000 Blacksmith, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.web; import java.io.InputStream; import java.util.Enumeration; import java.util.Locale; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import net.wotonomy.foundation.NSArray; import net.wotonomy.foundation.NSData; import net.wotonomy.foundation.NSDictionary; import net.wotonomy.foundation.NSMutableArray; import net.wotonomy.foundation.NSMutableData; import net.wotonomy.foundation.NSMutableDictionary; /** * A pure java implementation of WORequest. This implementation is backed by an * HttpServletRequest, and thus does not support application-specific * subclassing. Future implementations may remove this limitation. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 905 $ */ public class WORequest extends WOResponse { HttpServletRequest request; private WOApplication application; private NSArray languages; private String requestHandlerKey; private String requestHandlerPath; private String pageName; private String contextID; private String senderID; private String defaultFormValueEncoding; /** * Parameterless constructor which should not be called. */ public WORequest() { throw new RuntimeException("This constructor is not yet supported."); } /** * Standard constructor. Method and URL are required. HeaderMap is a map of * header names to arrays containing one or more values. Data is the content of * the request. UserInfo contains application-defined parameters that get passed * to all objects involved in dispatching the request. */ public WORequest(String aMethod, String aURL, String aProtocolName, NSDictionary> headerMap, NSData aData, NSDictionary userInfo) { throw new RuntimeException("This constructor is not yet supported."); } /** * The only supported constructor for this implementation. This WORequest will * wrap the specified servlet request. */ public WORequest(HttpServletRequest aRequest, WOApplication anApplication) { request = aRequest; application = anApplication; languages = null; senderID = null; pageName = null; contextID = null; defaultFormValueEncoding = "ISO8859_1"; requestHandlerKey = WOApplication.directActionRequestHandlerKey(); requestHandlerPath = request.getServletPath(); // request.getPathInfo(); String remainder = requestHandlerPath; if (requestHandlerPath == null) requestHandlerPath = ""; if (requestHandlerPath.length() > 0) { int i; // get request handler key i = requestHandlerPath.indexOf("/", 1); if (i != -1) { requestHandlerKey = requestHandlerPath.substring(1, i); requestHandlerPath = requestHandlerPath.substring(i); } Enumeration e = requestHandlerPathArray().objectEnumerator(); // skipping session ID for now if (e.hasMoreElements()) { pageName = e.nextElement().toString(); } if (e.hasMoreElements()) { contextID = e.nextElement().toString(); } if (e.hasMoreElements()) { senderID = e.nextElement().toString(); } } } /** * Returns the HTTP method of the request: "GET" or "POST", or possibly "PUT". */ public String method() { return request.getMethod(); } /** * Returns the Uniform Resource Identifier, which is the part of the request URL * after the protocol name (after the port number) and before the query * parameters (before the question mark). */ public String uri() { return request.getRequestURI(); } /** * Returns the name of the protocol (presumably HTTP) and the version used by * the client as sent in the request headers. */ @Override public String httpVersion() { return request.getProtocol(); } /** * Returns an array of the header names in this request in no particular order. */ @Override public NSArray headerKeys() { return arrayFromEnumeration(request.getHeaderNames()); } /** * Returns an array of the header values for the specified key in no particular * order. */ @Override public NSArray headersForKey(String aString) { return arrayFromEnumeration(request.getHeaders(aString)); } /** * Returns one value for the specified header key. Provided as a convenience * since most headers have only one value. Returns null if not found. */ @Override public String headerForKey(String aKey) { return request.getHeader(aKey); } /** * Returns the content of the request, or null if no content. The type of the * content is determined by the "content-type" header. On error, null is * returned. */ @Override public NSData content() { // TODO: This is broken! NSMutableData data = new NSMutableData(); try { byte[] buf = new byte[request.getContentLength()]; InputStream is = request.getInputStream(); // this is so very not efficient int len; while ((len = is.read(buf)) > -1) { // copies data twice... data.appendData(new NSData(buf, 0, len)); } } catch (Exception exc) { return null; } return data; } /** * Returns the application-specific userInfo dictionary. This implementation * returns the servlet attribute map. */ @Override public NSDictionary userInfo() { // TODO: Test this logic. Object key, value; NSMutableDictionary info = new NSMutableDictionary<>(); java.util.Enumeration e = request.getAttributeNames(); while (e.hasMoreElements()) { key = e.nextElement(); value = request.getAttribute(e.nextElement().toString()); if (value != null) { info.setObjectForKey(value, key); } } return info; } /** * Returns the items in the request handler path parsed by the "/" delimiter and * put into an array. */ public NSArray requestHandlerPathArray() { return NSArray.componentsSeparatedByString(requestHandlerPath(), "/"); } /** * Returns the client's preferred languages in decreasing order of preference. * The strings are returned in java Locale format, meaning dashes (-) are * converted to underbars (_): see java.util.Locale.toString(). */ public NSArray browserLanguages() { if (languages == null) { languages = arrayFromEnumeration(request.getLocales()); } return languages; } /** * Returns the portion of the URI specifying the engine within which this web * application is running. This is important for generating URLs to be used in * the content of the response. */ public String adaptorPrefix() { // TODO: Test this logic. String name = request.getServletPath(); return name; // String uri = request.getRequestURI(); // int end = uri.indexOf( request.getPathInfo() ); // return uri.substring( request.getContextPath().length(), end ); } /** * Returns the application name as specified in the request URI. Note that * wotonomy web applications do not typically have a .woa extension, but the * extension will be included for those that do. */ public String applicationName() { return request.getContextPath(); } /** * Returns the id of the application instance that is needed to service this * request. -1 indicates that the request is not specific to a particular * instance of the application. This implementation currently returns -1. */ public int applicationNumber() { return -1; } /** * Returns the portion of the URI that specifies which request handler should * handle the request. */ public String requestHandlerKey() { return requestHandlerKey; } /** * Returns the portion of the URI that specifies path information for the * request, not including the query string. */ public String requestHandlerPath() { return requestHandlerPath; } /** * Returns the unique identifier for the sessions associated with this request, * or null if no session is found. */ public String sessionID() { HttpSession session = request.getSession(false); if (session == null) return null; return session.getId(); } /** * Returns an array containing all the form keys in the request. */ public NSArray formValueKeys() { return arrayFromEnumeration(request.getParameterNames()); } /** * Returns an array of the values for the specified key in no particular order. */ public NSArray formValuesForKey(String aKey) { NSMutableArray result = new NSMutableArray<>(); String[] values = request.getParameterValues(aKey); if (values != null) { for (int i = 0; i < values.length; i++) { result.addObject(values[i]); } } return result; } /** * Returns one value for the specified key. Provided as a convenience since most * keys have only one value. Returns null if not found. */ public Object formValueForKey(String aKey) { return request.getParameter(aKey); } /** * Returns the value for the specified key, as a String. */ public String stringFormValueForKey(String key) { Object x = formValueForKey(key); if (x != null) return x.toString(); return null; } /** * Returns a dictionary containing all the key-value mappings in the request. */ public NSDictionary> formValues() { NSMutableDictionary> result = new NSMutableDictionary<>(); java.util.Enumeration e = request.getParameterNames(); String key; while (e.hasMoreElements()) { key = e.nextElement().toString(); result.setObjectForKey(formValuesForKey(key), key); } return result; } /** * Returns whether the request is from a java-based client component. This * implementation returns false. */ public boolean isFromClientComponent() { return false; } /** * Returns an array of cookie values for the specified key in no particular * order. */ public NSArray cookieValuesForKey(String aKey) { // TODO: Test this logic. NSMutableArray result = new NSMutableArray<>(); Cookie[] cookies = request.getCookies(); if (cookies == null) return result; for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals(aKey)) { result.addObject(cookies[i].getValue()); } } return result; } /** * Returns one value for the specified cookie key. Provided as a convenience * since most cookies have only one value. Returns null if not found. */ public String cookieValueForKey(String aKey) { // TODO: Test this logic. Cookie[] cookies = request.getCookies(); if (cookies == null) return null; for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals(aKey)) { return cookies[i].getValue(); } } return null; } /** * Returns a dictionary of cookie key-value mappings. */ public NSDictionary> cookieValues() { // TODO: Test this logic. NSMutableDictionary> result = new NSMutableDictionary<>(); Cookie[] cookies = request.getCookies(); if (cookies == null) return result; NSMutableArray value; for (int i = 0; i < cookies.length; i++) { value = (NSMutableArray) result.objectForKey(cookies[i].getName()); if (value == null) { value = new NSMutableArray<>(); result.setObjectForKey(value, cookies[i].getName()); } value.addObject(cookies[i].getValue()); } return result; } /** * Sets the default character encoding. */ public void setDefaultFormValueEncoding(String encoding) { defaultFormValueEncoding = encoding; } /** * Returns the default form value encoding ("ISO8859_1"). */ public String defaultFormValueEncoding() { return defaultFormValueEncoding; } /** * Sets whether the appropriate encoding scheme for decoding the form values * will be automatically determined. */ public void setFormValueEncodingDetectionEnabled(boolean enabled) { throw new RuntimeException("Not yet implemented."); } /** * Gets whether the appropriate encoding scheme for decoding the form values is * currently automatically determined. */ public boolean isFormValueEncodingDetectionEnabled() { throw new RuntimeException("Not yet implemented."); } /** * Gets the current method used for decoding form values. */ public int formValueEncoding() { throw new RuntimeException("Not yet implemented."); } /** * Returns the host name of the server that is the target of this request. NOTE: * This method is not published in the WORequest specification. */ String applicationHost() { // FIXME: this should call WOApplication.hostname(); return request.getServerName(); } /** * Returns the port of the server that is the target of this request. NOTE: This * method is not published in the WORequest specification. */ int port() { // FIXME: this should call WOApplication.port(); return request.getServerPort(); } /** * Returns the backing HttpServletRequest. */ HttpServletRequest servletRequest() { return request; } /** * Returns the application that was the target of this request. This method is * not published in the WORequest specification. */ WOApplication application() { return application; } /** * This method is not published in the WORequest specification. This sender id * from the URI, which is the id of the element that generated this request. */ String senderID() { return senderID; } /** * This method is not published in the WORequest specification. This returns the * context id from the URI. */ String contextID() { return contextID; } /** * This method is not published in the WORequest specification. */ String pageName() { return pageName; } /** * Convenience method to populate an NSArray from an Enumeration. */ private static NSArray arrayFromEnumeration(java.util.Enumeration e) { NSMutableArray result = new NSMutableArray<>(); while (e.hasMoreElements()) { result.addObject(e.nextElement()); } return result; } }