/* 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 javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.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 paramters 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. */ public String httpVersion () { return request.getProtocol(); } /** * Returns an array of the header names in this request * in no particular order. */ public NSArray headerKeys () { return arrayFromEnumeration( request.getHeaderNames() ); } /** * Returns an array of the header values for the specified key * in no particular order. */ 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. */ 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. */ 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. */ 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( cookies[i].getValue(), 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; } }