View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Blacksmith, Inc.
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.web;
20  
21  import java.io.InputStream;
22  import java.util.Enumeration;
23  
24  import javax.servlet.http.Cookie;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpSession;
27  
28  import net.wotonomy.foundation.NSArray;
29  import net.wotonomy.foundation.NSData;
30  import net.wotonomy.foundation.NSDictionary;
31  import net.wotonomy.foundation.NSMutableArray;
32  import net.wotonomy.foundation.NSMutableData;
33  import net.wotonomy.foundation.NSMutableDictionary;
34  
35  /***
36  * A pure java implementation of WORequest.
37  * This implementation is backed by an HttpServletRequest,
38  * and thus does not support application-specific subclassing.
39  * Future implementations may remove this limitation.
40  *
41  * @author michael@mpowers.net
42  * @author $Author: cgruber $
43  * @version $Revision: 905 $
44  */
45  public class WORequest extends WOResponse
46  {
47      HttpServletRequest request;
48      private WOApplication application;
49      private NSArray languages;
50      private String requestHandlerKey;
51      private String requestHandlerPath;
52      private String pageName;
53      private String contextID;
54      private String senderID;
55      private String defaultFormValueEncoding;
56  
57  	/***
58  	* Parameterless constructor which should not be called.
59  	*/
60      public WORequest ()
61      {
62  		throw new RuntimeException( 
63  			"This constructor is not yet supported." );
64      }
65  
66  	/***
67  	* Standard constructor.  Method and URL are required.
68  	* HeaderMap is a map of header names to arrays containing
69  	* one or more values.  Data is the content of the request.
70  	* UserInfo contains application-defined paramters that get 
71  	* passed to all objects involved in dispatching the request.
72  	*/
73      public WORequest 
74      	(String aMethod, String aURL, String aProtocolName, 
75  	    NSDictionary headerMap, NSData aData, NSDictionary userInfo)
76  	{
77  		throw new RuntimeException( 
78  			"This constructor is not yet supported." );
79  	}
80  	
81  	/***
82  	* The only supported constructor for this implementation.
83  	* This WORequest will wrap the specified servlet request.
84  	*/
85  	public WORequest( HttpServletRequest aRequest, WOApplication anApplication )
86  	{
87  		request = aRequest;
88          application = anApplication;
89          
90          languages = null;
91          senderID = null;
92          pageName = null;
93          contextID = null;
94          defaultFormValueEncoding = "ISO8859_1";
95          requestHandlerKey = WOApplication.directActionRequestHandlerKey();
96          requestHandlerPath = request.getServletPath(); // request.getPathInfo();
97          String remainder = requestHandlerPath;
98          if ( requestHandlerPath == null ) requestHandlerPath = "";
99          if ( requestHandlerPath.length() > 0 )
100         {
101             int i;
102             
103             // get request handler key
104             i = requestHandlerPath.indexOf( "/", 1 );
105             if ( i != -1 )
106             {
107                 requestHandlerKey = requestHandlerPath.substring( 1, i );
108                 requestHandlerPath = requestHandlerPath.substring( i );
109             }
110             
111             Enumeration e = requestHandlerPathArray().objectEnumerator();
112             // skipping session ID for now
113             if ( e.hasMoreElements() )
114             {
115                 pageName = e.nextElement().toString();
116             }
117             if ( e.hasMoreElements() )
118             {
119                 contextID = e.nextElement().toString();
120             }
121             if ( e.hasMoreElements() )
122             {
123                 senderID = e.nextElement().toString();
124             }
125         }
126 	}
127 
128     /***
129     * Returns the HTTP method of the request: "GET" or "POST", 
130     * or possibly "PUT".
131     */
132     public String method ()
133     {
134     	return request.getMethod();
135     }
136     
137     /***
138     * Returns the Uniform Resource Identifier, which is the part
139     * of the request URL after the protocol name (after the port
140     * number) and before the query parameters (before the question mark).
141     */
142     public String uri ()
143     {
144     	return request.getRequestURI();
145     }
146 
147 	/***
148 	* Returns the name of the protocol (presumably HTTP) and the
149 	* version used by the client as sent in the request headers.
150 	*/    
151     public String httpVersion ()
152     {
153     	return request.getProtocol();
154     }
155     
156     /***
157     * Returns an array of the header names in this request
158     * in no particular order.
159     */
160     public NSArray headerKeys ()
161     {
162     	return arrayFromEnumeration( request.getHeaderNames() );	
163     }
164     
165     /***
166     * Returns an array of the header values for the specified key
167     * in no particular order.
168     */
169     public NSArray headersForKey (String aString)
170     {
171     	return arrayFromEnumeration( request.getHeaders( aString ) );
172     }
173     
174     /***
175     * Returns one value for the specified header key.
176     * Provided as a convenience since most headers have
177     * only one value.  Returns null if not found.
178     */
179     public String headerForKey (String aKey)
180     {
181     	return request.getHeader( aKey );
182     }
183     
184     /***
185     * Returns the content of the request, or null if no content.
186     * The type of the content is determined by the "content-type" header.
187     * On error, null is returned.
188     */
189     public NSData content ()
190     {
191     	//TODO: This is broken!
192     	NSMutableData data = new NSMutableData();
193     	try
194 	    {
195 	    	byte[] buf = new byte[ request.getContentLength() ];
196 		    InputStream is = request.getInputStream();
197 			
198 			// this is so very not efficient
199 			int len;
200 		    while ( ( len = is.read( buf ) ) > -1 )
201 		    {
202 			    // copies data twice...
203 		    	data.appendData( new NSData( buf, 0, len ) ); 
204 		    }
205 		}
206 		catch ( Exception exc )
207 		{
208 			return null;
209 		}
210 		return data;
211     }
212     
213     /***
214     * Returns the application-specific userInfo dictionary.
215     * This implementation returns the servlet attribute map.
216     */
217     public NSDictionary userInfo ()
218     {
219     	//TODO: Test this logic.
220     	Object key, value;
221     	NSMutableDictionary info = new NSMutableDictionary();
222 	    java.util.Enumeration e = request.getAttributeNames();
223 	    while ( e.hasMoreElements() )
224 	    {
225 	    	key = e.nextElement();
226 		    value = request.getAttribute( e.nextElement().toString() );
227 		    if ( value != null )
228 		    {
229 		    	info.setObjectForKey( value, key ); 
230 		    }
231 	    }
232 	    return info;
233     }
234     
235     /***
236     * Returns the items in the request handler path parsed
237     * by the "/" delimiter and put into an array.
238     */
239     public NSArray requestHandlerPathArray ()
240     {
241     	return NSArray.componentsSeparatedByString( requestHandlerPath(), "/" );
242     }
243     
244     /***
245     * Returns the client's preferred languages in decreasing 
246     * order of preference.  The strings are returned in java
247     * Locale format, meaning dashes (-) are converted to 
248     * underbars (_): see java.util.Locale.toString().
249     */
250     public NSArray browserLanguages ()
251     {
252         if ( languages == null )
253         {
254         	languages = arrayFromEnumeration( request.getLocales() );
255         }
256         return languages;
257     }
258     
259     /***
260     * Returns the portion of the URI specifying the engine within which
261     * this web application is running.  This is important for generating
262     * URLs to be used in the content of the response.
263     */
264     public String adaptorPrefix ()
265     {
266     	//TODO: Test this logic.
267     	String name = request.getServletPath();
268         return name;
269         
270 //	    String uri = request.getRequestURI();
271 //	    int end = uri.indexOf( request.getPathInfo() );
272 //    	return uri.substring( request.getContextPath().length(), end );
273     }
274     
275     /***
276     * Returns the application name as specified in the request URI.
277     * Note that wotonomy web applications do not typically have a .woa
278     * extension, but the extension will be included for those that do.
279     */
280     public String applicationName ()
281     {
282         return request.getContextPath();
283     }
284     
285     /***
286     * Returns the id of the application instance that is needed to 
287     * service this request.  -1 indicates that the request is not 
288     * specific to a particular instance of the application.
289     * This implementation currently returns -1.
290     */
291     public int applicationNumber ()
292     {
293 		return -1;
294     }
295     
296     /***
297     * Returns the portion of the URI that specifies which request handler
298     * should handle the request.  
299     */
300     public String requestHandlerKey ()
301     {
302         return requestHandlerKey;
303    }
304     
305     /***
306     * Returns the portion of the URI that specifies path information 
307     * for the request, not including the query string.
308     */
309     public String requestHandlerPath ()
310     {
311         return requestHandlerPath;
312     }
313     
314     /***
315     * Returns the unique identifier for the sessions associated with 
316     * this request, or null if no session is found.
317     */
318     public String sessionID ()
319     {
320     	HttpSession session = request.getSession( false );
321 	    if ( session == null ) return null;
322 	    return session.getId();
323     }
324     
325     /***
326     * Returns an array containing all the form keys in the request.
327     */
328     public NSArray formValueKeys ()
329     {
330     	return arrayFromEnumeration( request.getParameterNames() );
331     }
332     
333     /***
334     * Returns an array of the values for the specified key in
335     * no particular order.
336     */
337     public NSArray formValuesForKey (String aKey)
338     {
339     	NSMutableArray result = new NSMutableArray();
340     	String[] values = request.getParameterValues( aKey );
341         if ( values != null )
342         {
343             for ( int i = 0; i < values.length; i++ )
344             {
345                 result.addObject( values[i] );
346             }
347         }
348 	    return result;
349     }
350     
351     /***
352     * Returns one value for the specified key.
353     * Provided as a convenience since most keys have
354     * only one value.  Returns null if not found.
355     */
356     public Object formValueForKey (String aKey)
357     {
358     	return request.getParameter( aKey );
359     }
360 
361     /***
362     * Returns the value for the specified key, as a String.
363     */
364     public String stringFormValueForKey(String key) {
365         Object x = formValueForKey(key);
366         if (x != null)
367             return x.toString();
368         return null;
369     }
370 
371     /***
372     * Returns a dictionary containing all the key-value 
373     * mappings in the request.
374     */
375     public NSDictionary formValues ()
376     {
377     	NSMutableDictionary result = new NSMutableDictionary ();
378     	java.util.Enumeration e = request.getParameterNames();
379 	    String key;
380 	    while ( e.hasMoreElements() )
381 	    {
382 	    	key = e.nextElement().toString();
383 	    	result.setObjectForKey( formValuesForKey( key ), key );
384 	    }
385 	    return result;
386     }
387     
388     /***
389     * Returns whether the request is from a java-based client component.
390     * This implementation returns false.
391     */
392     public boolean isFromClientComponent ()
393     {
394     	return false;
395     }
396     
397     /***
398     * Returns an array of cookie values for the specified key in
399     * no particular order.
400     */
401     public NSArray cookieValuesForKey (String aKey)
402     {
403     	// TODO: Test this logic.
404     	NSMutableArray result = new NSMutableArray();
405     	Cookie[] cookies = request.getCookies();
406 	    if ( cookies == null ) return result;
407 	    
408 	    for ( int i = 0; i < cookies.length; i++ )
409 	    {
410 	    	if ( cookies[i].getName().equals( aKey ) )
411 		    {
412 		    	result.addObject( cookies[i].getValue() );
413 		    } 
414 	    }
415 	    
416 	    return result;
417     }
418     
419     /***
420     * Returns one value for the specified cookie key.
421     * Provided as a convenience since most cookies have
422     * only one value.  Returns null if not found.
423     */
424     public String cookieValueForKey (String aKey)
425     {
426     	// TODO: Test this logic.
427     	Cookie[] cookies = request.getCookies();
428 	    if ( cookies == null ) return null;
429 	    
430 	    for ( int i = 0; i < cookies.length; i++ )
431 	    {
432 	    	if ( cookies[i].getName().equals( aKey ) )
433 		    {
434 		    	return cookies[i].getValue();
435 		    } 
436 	    }
437 	    
438 	    return null;
439     }
440     
441     /***
442     * Returns a dictionary of cookie key-value mappings. 
443     */
444     public NSDictionary cookieValues ()
445     {
446     	// TODO: Test this logic.
447     	NSMutableDictionary result = new NSMutableDictionary();
448     	Cookie[] cookies = request.getCookies();
449 	    if ( cookies == null ) return result;
450 	    
451 	    NSMutableArray value;
452 	    for ( int i = 0; i < cookies.length; i++ )
453 	    {
454 	    	value = (NSMutableArray) result.objectForKey( 
455 		    	cookies[i].getName() );
456 		    if ( value == null ) 
457 		    {
458 		    	value = new NSMutableArray();
459                 result.setObjectForKey( 
460 					cookies[i].getValue(), cookies[i].getName() );
461 			}
462 		    value.addObject( cookies[i].getValue() );
463 	    }
464 	    
465 	    return result;
466     }
467 
468     /***
469     * Sets the default character encoding.
470     */
471     public void setDefaultFormValueEncoding (String encoding)
472     {
473     	defaultFormValueEncoding = encoding;
474     }
475     
476     /***
477     * Returns the default form value encoding ("ISO8859_1").
478     */
479     public String defaultFormValueEncoding ()
480     {
481     	return defaultFormValueEncoding;
482     }
483     
484     /***
485     * Sets whether the appropriate encoding scheme for decoding
486     * the form values will be automatically determined.
487     */
488     public void setFormValueEncodingDetectionEnabled (boolean enabled)
489     {
490     	throw new RuntimeException( "Not yet implemented." );
491     }
492     
493     /***
494     * Gets whether the appropriate encoding scheme for decoding
495     * the form values is currently automatically determined.
496     */
497     public boolean isFormValueEncodingDetectionEnabled ()
498     {
499     	throw new RuntimeException( "Not yet implemented." );
500     }
501     
502     /***
503     * Gets the current method used for decoding form values.
504     */
505     public int formValueEncoding ()
506     {
507     	throw new RuntimeException( "Not yet implemented." );
508     }
509     
510     /***
511     * Returns the host name of the server that is the target of this request.
512     * NOTE: This method is not published in the WORequest specification.
513     */
514     String applicationHost ()
515     {
516         // FIXME: this should call WOApplication.hostname();
517         return request.getServerName();
518     }
519     
520     /***
521     * Returns the port of the server that is the target of this request.
522     * NOTE: This method is not published in the WORequest specification.
523     */
524     int port()
525     {
526         // FIXME: this should call WOApplication.port();
527         return request.getServerPort();
528     }
529     
530     /***
531     * Returns the backing HttpServletRequest.
532     */
533     HttpServletRequest servletRequest ()
534     {
535     	return request;
536     }
537     
538     /***
539     * Returns the application that was the target of this request.
540     * This method is not published in the WORequest specification.
541     */
542     WOApplication application ()
543     {
544     	return application;
545     }
546     
547     /***
548     * This method is not published in the WORequest specification.
549     * This sender id from the URI, which is the id of the element
550     * that generated this request.
551     */
552     String senderID ()
553     {
554     	return senderID;
555     }
556     
557     /***
558     * This method is not published in the WORequest specification.
559     * This returns the context id from the URI.
560     */
561     String contextID ()
562     {
563     	return contextID;
564     }
565     
566     /***
567     * This method is not published in the WORequest specification.
568     */
569     String pageName ()
570     {
571     	return pageName;
572     }
573     
574     /***
575     * Convenience method to populate an NSArray from an Enumeration.
576     */
577     private static NSArray arrayFromEnumeration( java.util.Enumeration e )
578     {
579     	NSMutableArray result = new NSMutableArray();
580     	while ( e.hasMoreElements() )
581 	    {
582 	    	result.addObject( e.nextElement() );
583 	    }
584 	    return result;
585     }
586 }