View Javadoc

1   package org.apache.turbine.util.parser;
2   
3   /*
4    * Copyright 2001-2004 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.net.URLDecoder;
20  
21  import java.util.Enumeration;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.Map;
25  import java.util.StringTokenizer;
26  
27  import javax.servlet.http.HttpServletRequest;
28  
29  import org.apache.commons.fileupload.FileItem;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import org.apache.turbine.services.upload.TurbineUpload;
35  import org.apache.turbine.services.upload.UploadService;
36  
37  import org.apache.turbine.util.TurbineException;
38  import org.apache.turbine.util.pool.Recyclable;
39  
40  /***
41   * DefaultParameterParser is a utility object to handle parsing and
42   * retrieving the data passed via the GET/POST/PATH_INFO arguments.
43   *
44   * <p>NOTE: The name= portion of a name=value pair may be converted
45   * to lowercase or uppercase when the object is initialized and when
46   * new data is added.  This behaviour is determined by the url.case.folding
47   * property in TurbineResources.properties.  Adding a name/value pair may
48   * overwrite existing name=value pairs if the names match:
49   *
50   * <pre>
51   * ParameterParser pp = data.getParameters();
52   * pp.add("ERROR",1);
53   * pp.add("eRrOr",2);
54   * int result = pp.getInt("ERROR");
55   * </pre>
56   *
57   * In the above example, result is 2.
58   *
59   * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
60   * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
61   * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
62   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
63   * @version $Id: DefaultParameterParser.java,v 1.20.2.2 2004/05/20 03:33:43 seade Exp $
64   */
65  public class DefaultParameterParser
66      extends BaseValueParser
67      implements ParameterParser, Recyclable
68  {
69      /*** Logging */
70      private static Log log = LogFactory.getLog(DefaultParameterParser.class);
71  
72      /*** The servlet request to parse. */
73      private HttpServletRequest request = null;
74  
75      /*** The raw data of a file upload. */
76      private byte[] uploadData = null;
77  
78      /*** Map of request parameters to FileItem[]'s */
79      private Map fileParameters = new HashMap();
80  
81      /*** Turbine Upload Service reference */
82      private static UploadService uploadService = null;
83  
84      /*** Do we have an upload Service? */
85      private static boolean uploadServiceIsAvailable = false;
86  
87      /***
88       * Create a new empty instance of ParameterParser.  Uses the
89       * default character encoding (US-ASCII).
90       *
91       * <p>To add name/value pairs to this set of parameters, use the
92       * <code>add()</code> methods.
93       */
94      public DefaultParameterParser()
95      {
96          super();
97          configureUploadService();
98      }
99  
100     /***
101      * Create a new empty instance of ParameterParser. Takes a
102      * character encoding name to use when converting strings to
103      * bytes.
104      *
105      * <p>To add name/value pairs to this set of parameters, use the
106      * <code>add()</code> methods.
107      *
108      * @param characterEncoding The character encoding of strings.
109      */
110     public DefaultParameterParser(String characterEncoding)
111     {
112         super (characterEncoding);
113         configureUploadService();
114     }
115 
116     /***
117      * Checks for availability of the Upload Service. We do this
118      * check only once at Startup, because the getService() call
119      * is really expensive and we don't have to run it every time
120      * we process a request.
121      */
122     private void configureUploadService()
123     {
124         uploadServiceIsAvailable = TurbineUpload.isAvailable();
125         if (uploadServiceIsAvailable)
126         {
127             uploadService = TurbineUpload.getService();
128         }
129     }
130 
131     /***
132      * Disposes the parser.
133      */
134     public void dispose()
135     {
136         this.request = null;
137         this.uploadData = null;
138         this.fileParameters.clear();
139         super.dispose();
140     }
141 
142     /***
143      * Gets the parsed servlet request.
144      *
145      * @return the parsed servlet request or null.
146      */
147     public HttpServletRequest getRequest()
148     {
149         return this.request;
150     }
151 
152     /***
153      * Sets the servlet request to be parser.  This requires a
154      * valid HttpServletRequest object.  It will attempt to parse out
155      * the GET/POST/PATH_INFO data and store the data into a Map.
156      * There are convenience methods for retrieving the data as a
157      * number of different datatypes.  The PATH_INFO data must be a
158      * URLEncoded() string.
159      * <p>
160      * To add name/value pairs to this set of parameters, use the
161      * <code>add()</code> methods.
162      *
163      * @param request An HttpServletRequest.
164      */
165     public void setRequest(HttpServletRequest request)
166     {
167         clear();
168 
169         uploadData = null;
170 
171         String enc = request.getCharacterEncoding();
172         setCharacterEncoding(enc != null ? enc : "US-ASCII");
173 
174         // String object re-use at its best.
175         String tmp = null;
176 
177         tmp = request.getHeader("Content-type");
178 
179         if (uploadServiceIsAvailable
180                 && uploadService.getAutomatic()
181                 && tmp != null
182                 && tmp.startsWith("multipart/form-data"))
183         {
184             log.debug("Running the Turbine Upload Service");
185             try
186             {
187                 TurbineUpload.parseRequest(request, this);
188             }
189             catch (TurbineException e)
190             {
191                 log.error("File upload failed", e);
192             }
193         }
194 
195         for (Enumeration names = request.getParameterNames();
196              names.hasMoreElements();)
197         {
198             tmp = (String) names.nextElement();
199             add(convert(tmp),
200                     request.getParameterValues(tmp));
201         }
202 
203         // Also cache any pathinfo variables that are passed around as
204         // if they are query string data.
205         try
206         {
207             StringTokenizer st =
208                     new StringTokenizer(request.getPathInfo(), "/");
209             boolean isNameTok = true;
210             String pathPart = null;
211             while (st.hasMoreTokens())
212             {
213                 if (isNameTok)
214                 {
215                     tmp = URLDecoder.decode(st.nextToken());
216                     isNameTok = false;
217                 }
218                 else
219                 {
220                     pathPart = URLDecoder.decode(st.nextToken());
221                     if (tmp.length() > 0)
222                     {
223                         add(convert(tmp), pathPart);
224                     }
225                     isNameTok = true;
226                 }
227             }
228         }
229         catch (Exception e)
230         {
231             // If anything goes wrong above, don't worry about it.
232             // Chances are that the path info was wrong anyways and
233             // things that depend on it being right will fail later
234             // and should be caught later.
235         }
236 
237         this.request = request;
238 
239         if (log.isDebugEnabled())
240         {
241             log.debug("Parameters found in the Request:");
242             for (Iterator it = keySet().iterator(); it.hasNext();)
243             {
244                 String key = (String) it.next();
245                 log.debug("Key: " + key + " -> " + getString(key));
246             }
247         }
248     }
249 
250     /***
251      * Sets the uploadData byte[]
252      *
253      * @param uploadData A byte[] with data.
254      */
255     public void setUploadData(byte[] uploadData)
256     {
257         this.uploadData = uploadData;
258     }
259 
260     /***
261      * Gets the uploadData byte[]
262      *
263      * @return uploadData A byte[] with data.
264      */
265     public byte[] getUploadData()
266     {
267         return this.uploadData;
268     }
269 
270     /***
271      * Add a FileItem object as a parameters.  If there are any
272      * FileItems already associated with the name, append to the
273      * array.  The reason for this is that RFC 1867 allows multiple
274      * files to be associated with single HTML input element.
275      *
276      * @param name A String with the name.
277      * @param value A FileItem with the value.
278      */
279     public void append(String name, FileItem value)
280     {
281         FileItem[] items = this.getFileItems(name);
282         if (items == null)
283         {
284             items = new FileItem[1];
285             items[0] = value;
286             fileParameters.put(convert(name), items);
287         }
288         else
289         {
290             FileItem[] newItems = new FileItem[items.length + 1];
291             System.arraycopy(items, 0, newItems, 0, items.length);
292             newItems[items.length] = value;
293             fileParameters.put(convert(name), newItems);
294         }
295     }
296 
297     /***
298      * Return a FileItem object for the given name.  If the name does
299      * not exist or the object stored is not a FileItem, return null.
300      *
301      * @param name A String with the name.
302      * @return A FileItem.
303      */
304     public FileItem getFileItem(String name)
305     {
306         try
307         {
308             FileItem value = null;
309             Object object = fileParameters.get(convert(name));
310             if (object != null)
311             {
312                 value = ((FileItem[]) object)[0];
313             }
314             return value;
315         }
316         catch (ClassCastException e)
317         {
318             log.error("Parameter ("
319                     + name + ") is not an instance of FileItem", e);
320             return null;
321         }
322     }
323 
324     /***
325      * Return an array of FileItem objects for the given name.  If the
326      * name does not exist or the object stored is not a FileItem
327      * array, return null.
328      *
329      * @param name A String with the name.
330      * @return A FileItem[].
331      */
332     public FileItem[] getFileItems(String name)
333     {
334         try
335         {
336             return (FileItem[]) fileParameters.get(convert(name));
337         }
338         catch (ClassCastException e)
339         {
340             log.error("Parameter ("
341                     + name + ") is not an instance of FileItem[]", e);
342             return null;
343         }
344     }
345 }