001: /*
002: * The contents of this file are subject to the
003: * Mozilla Public License Version 1.1 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
009: * See the License for the specific language governing rights and
010: * limitations under the License.
011: *
012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
014: *
015: * All Rights Reserved.
016: *
017: * Contributor(s):
018: */
019: package org.openharmonise.rm.view.servlet.utils;
020:
021: import com.oreilly.servlet.*;
022:
023: import java.io.*;
024:
025: import java.util.*;
026: import java.util.logging.*;
027: import java.util.logging.Logger;
028:
029: import javax.servlet.http.*;
030:
031: /**
032: * This class provides a single interface to a <code>HttpServletRequest</code>
033: * no matter whether the underlying request is a multipart request or not.
034: *
035: * @author Michael Bell
036: * @version $Revision: 1.1 $
037: *
038: */
039: public class HttpRequestManager {
040:
041: /**
042: * <code>String</code> constant for the first 4 characters of a text based.
043: * mime-type.
044: */
045: protected static final String TYPE_TEXT = "text";
046:
047: /**
048: * <code>String</code> constant for the HTTP 'User-Agent' header parameter.
049: */
050: public static final String HEADER_USER_AGENT = "user-Agent";
051:
052: /**
053: * <code>String</code> constant for the HTTP 'Referer' header parameter.
054: */
055: public static final String HEADER_REFERER = "referer";
056:
057: /**
058: * <code>String</code> constant for the HTTP 'Content-Type' header parameter.
059: */
060: public static final String HEADER_CONTENT_TYPE = "content-type";
061:
062: /**
063: * <code>String</code> constant for the configuration parameter that sets
064: * the maximum file size for upload.
065: */
066: static final String MAXFILESIZE_PNAME = "MAX_UPLOADED_FILESIZE_MB";
067: /**
068: * <code>String</code> constant for the configuration parameter that sets
069: * the directory which uploaded files are uploaded to.
070: */
071: static final String TEMPDIR_PNAME = "UPLOADED_TEMPFILE_DIR";
072:
073: /**
074: * <code>boolean</code> which indicates whether this request is a
075: * multipart request
076: */
077: private boolean m_is_multipart_request = false;
078:
079: /**
080: * <code>boolean</code> which indicates whether the uploaded file is a binary
081: * file.
082: */
083: private boolean m_upload_is_binary = false;
084:
085: /**
086: * <code>boolean</code> which indicates whether the uploaded file is an XML
087: * document.
088: */
089: private boolean m_contains_xml_feed = false;
090:
091: /**
092: * A non multipart request.
093: */
094: private HttpServletRequest m_nonmulti_request;
095:
096: /**
097: * A multipart request
098: */
099: private MultipartRequest m_multi_request;
100:
101: /**
102: * <code>boolean</code> which indicates whether the uploaded file has
103: * been deleted.
104: */
105: private boolean m_uploaded_file_isdeleted;
106:
107: /**
108: * <code>Map</code> of HTTP header parameter names to values.
109: */
110: private HashMap m_headers = new HashMap();
111:
112: /**
113: * Cached value of the remote address making the request.
114: */
115: private String m_sRemoteAddress = null;
116:
117: /**
118: * The maximum file size this request will accept.
119: */
120: int m_MAX_FILESIZE;
121:
122: /**
123: * The path of directory which holds uploaded files.
124: */
125: String m_TEMP_FILE_DIR;
126:
127: /**
128: * Logger for this class
129: */
130: private static final Logger m_logger = Logger
131: .getLogger(HttpRequestManager.class.getName());
132:
133: /**
134: * Constructs a <code>HttpRequestManager</code> which can act as an interface
135: * to the given <code>HttpServletRequest</code>.
136: *
137: * @param request the servlet request
138: * @throws RequestManagerException if an error occurs handling the request
139: */
140: public HttpRequestManager(HttpServletRequest request, int max_filesize,
141: String tempFolder)
142: throws RequestManagerException {
143:
144: if (max_filesize <= 0) {
145: throw new IllegalArgumentException("File size must be greater than 0");
146: }
147:
148: try {
149: m_MAX_FILESIZE = max_filesize;
150:
151: m_TEMP_FILE_DIR = tempFolder;
152:
153: Enumeration enum = request.getHeaderNames();
154:
155: while(enum.hasMoreElements()) {
156: String sHeaderName = (String) enum.nextElement();
157:
158: m_headers.put(sHeaderName,request.getHeader(sHeaderName));
159: }
160:
161: m_sRemoteAddress = request.getRemoteAddr();
162:
163: String type = (String) m_headers.get(HEADER_CONTENT_TYPE);
164:
165: if ((type != null)
166: && type.toLowerCase().startsWith("multipart/form-data")) {
167:
168: m_is_multipart_request = true;
169:
170: // This saves the file(s) into the temporary folder
171: m_multi_request =
172: new MultipartRequest(
173: request,
174: m_TEMP_FILE_DIR,
175: m_MAX_FILESIZE);
176:
177: m_uploaded_file_isdeleted = false;
178: m_upload_is_binary = isContentBinary();
179:
180: } else if (
181: (type != null) && type.toLowerCase().startsWith("text/xml")) {
182: m_contains_xml_feed = true;
183: m_nonmulti_request = request;
184:
185: } else {
186: if (m_upload_is_binary) {
187: throw new RuntimeException(
188: "Upload cannot be set as binary, not multi-part form data ("
189: + type
190: + ")");
191: }
192:
193: m_is_multipart_request = false;
194: m_nonmulti_request = request;
195:
196: }
197: } catch (IOException e) {
198: throw new RequestManagerException(
199: "Error occured creating multipart request rep",
200: e);
201: }
202: }
203:
204: /**
205: * Returns a <code>Map</code> of header parameter names to values
206: * included in the request.
207: *
208: * @return a <code>Map</code> of header parameter names to values
209: */
210: public Map getHeaders() {
211: return m_headers;
212: }
213:
214: /**
215: * Returns the remote address making this request.
216: *
217: * @return the remote address making this request
218: */
219: public String getRemoteAddress() {
220: return m_sRemoteAddress;
221: }
222:
223: /**
224: * Returns <code>true</code> if the content attached to the request was binary.
225: *
226: * @return <code>true</code> if the content attached to the request was binary
227: */
228: public boolean isContentBinary() {
229: boolean bIsBinary = false;
230:
231: if (this .m_is_multipart_request == false) {
232: return false;
233: }
234:
235: Enumeration enum = this .m_multi_request.getFileNames();
236:
237: while (enum.hasMoreElements()) {
238: String filename = (String) enum.nextElement();
239:
240: if (m_multi_request.getContentType(filename).startsWith(TYPE_TEXT)
241: == false) {
242: bIsBinary = true;
243: }
244: }
245:
246: return bIsBinary;
247: }
248:
249: /**
250: * Returns <code>true</code> if request contains an XML document.
251: *
252: * @return <code>true</code> if request contains an XML document
253: */
254: public boolean containsXMLFeed() {
255: return m_contains_xml_feed;
256: }
257:
258: /**
259: * Returns the names of files which were included in the request.
260: *
261: * @return the names of files which were included in the request
262: */
263: public Enumeration getFileNames() {
264: if (m_is_multipart_request) {
265: return m_multi_request.getFileNames();
266: } else {
267: throw new RuntimeException("Not multipart form data");
268: }
269: }
270:
271: /**
272: * Returns the query string values for the specified parameter name.
273: *
274: * @param name the name of the query string parameter whose values are to be returned
275: * @return the query string values for the specified parameter name
276: */
277: public String[] getParameterValues(String name) {
278: if (m_is_multipart_request) {
279: return m_multi_request.getParameterValues(name);
280: } else {
281: return m_nonmulti_request.getParameterValues(name);
282: }
283: }
284:
285: /**
286: * Returns the query string value for the specified parameter name.
287: *
288: * @param name the name of the query string parameter whose value is to be returned
289: * @return the query string value for the specified parameter name
290: */
291: public String getParameter(String name) {
292: if (m_is_multipart_request) {
293: return m_multi_request.getParameter(name);
294: } else {
295: return m_nonmulti_request.getParameter(name);
296: }
297: }
298:
299: /**
300: * Returns an <code>Enumeration</code> of the query string parameter names.
301: *
302: * @return an <code>Enumeration</code> of the query string parameter names
303: */
304: public Enumeration getParameterNames() {
305: if (m_is_multipart_request) {
306: return m_multi_request.getParameterNames();
307: } else {
308: return m_nonmulti_request.getParameterNames();
309: }
310: }
311:
312: /**
313: * Moves the uploaded file attached to the request to the specified file
314: * location, returning <code>true</code> if successful.
315: *
316: * @param new_file the new location for uploaded file
317: * @return <code>true</code> if the operation was successful
318: * @throws IllegalArgumentException if the new file location is invalid
319: */
320: public boolean MoveUploadedFile(File new_file)
321: throws IllegalArgumentException {
322: if (!m_is_multipart_request) {
323: throw new RuntimeException(
324: "Cannot move file, request was not multipart form data");
325: }
326:
327: boolean file_was_moved = false;
328:
329: // Get the file that was uploaded
330: File uploaded_file = getUploadedFile();
331:
332: if (uploaded_file != null) {
333: String new_dir_txt = new_file.getAbsolutePath();
334:
335: // get dir
336: new_dir_txt = new_dir_txt.substring(0, new_dir_txt
337: .lastIndexOf(File.separator) + 1);
338:
339: File new_dir = new File(new_dir_txt);
340:
341: // check the new file is available
342: if (!new_dir.isDirectory()) {
343: throw new IllegalArgumentException("Not a directory : "
344: + new_dir);
345: }
346:
347: // Check new file is writable by us
348: if (!new_dir.canWrite()) {
349: throw new IllegalArgumentException("Not writable: "
350: + new_dir);
351: }
352:
353: // check there isn't already a file there, delete it if there is
354: if (new_file.exists()) {
355: if (false == new_file.delete()) {
356: throw new RuntimeException(
357: "Problem deleting existing file:"
358: + new_file);
359: }
360:
361: if (new_file.exists()) {
362: throw new RuntimeException(
363: "Problem deleting existing file "
364: + "(it still exists):" + new_file);
365: }
366: }
367:
368: if (!uploaded_file.canRead()) {
369: throw new RuntimeException("Cannot read uploaded file:"
370: + uploaded_file);
371: }
372:
373: int retries = 0;
374:
375: while (((file_was_moved = uploaded_file.renameTo(new_file)) == false)
376: && (retries < 10)) {
377: retries++;
378: // wait a few seconds, try again
379: int wait_for = 3 * 1000;
380:
381: try {
382: Thread.sleep(wait_for);
383: } catch (InterruptedException e) {
384: // we don't care about this exception
385: m_logger.log(Level.WARNING,
386: e.getLocalizedMessage(), e);
387: }
388: }
389:
390: if (!file_was_moved) {
391: throw new RuntimeException(
392: "Failed to rename uploaded_file:"
393: + uploaded_file + ", to:" + new_file
394: + " retried " + retries
395: + " times [known Java bug]");
396: }
397:
398: m_uploaded_file_isdeleted = true;
399:
400: if (!file_was_moved) {
401: throw new RuntimeException(
402: "Failed to rename uploaded file:"
403: + uploaded_file + ", to:" + new_file);
404: }
405:
406: } else {
407: file_was_moved = true;
408:
409: }
410:
411: return file_was_moved;
412: }
413:
414: /**
415: * Returns <code>true</code> if the request was of a multi-part content type.
416: *
417: * @return
418: */
419: public boolean hasUploadedFiles() {
420: if (m_is_multipart_request == false) {
421: return false;
422: }
423:
424: if (this .getUploadedFile() != null) {
425: return true;
426: } else {
427: return false;
428: }
429: }
430:
431: /**
432: * Returns the name of the uploaded file.
433: *
434: * @return
435: */
436: public String getUploadedFileName() {
437: if (m_is_multipart_request == false) {
438: return null;
439: }
440:
441: // get the uploaded file data
442: Enumeration uploaded_files = m_multi_request.getFileNames();
443: String uploaded_filename = null;
444:
445: if (uploaded_files.hasMoreElements()) {
446: uploaded_filename = (String) uploaded_files.nextElement();
447: } else {
448: throw new RuntimeException("No uploaded file");
449: }
450:
451: if (uploaded_files.hasMoreElements()) {
452: throw new RuntimeException("More than one uploaded file");
453: }
454:
455: return uploaded_filename;
456: }
457:
458: /**
459: * Returns the uploaded file.
460: *
461: * @return
462: */
463: public File getUploadedFile() {
464: if (!m_is_multipart_request) {
465: return null;
466: }
467:
468: String uploaded_filename = getUploadedFileName();
469: File uploaded_file = m_multi_request.getFile(uploaded_filename);
470:
471: if (uploaded_file != null) {
472: return uploaded_file.getAbsoluteFile();
473: } else {
474: return null;
475: }
476: }
477:
478: /**
479: * Returns the content of a muli-part content request as a <code>String</code>.
480: *
481: * @param delete_tmp_file
482: * @return
483: * @throws IOException
484: */
485: public String getContentAsString(boolean delete_tmp_file)
486: throws IOException {
487: String file_as_string = "";
488:
489: if (m_contains_xml_feed) {
490: file_as_string = getStringFromReqReader();
491: } else {
492: File uploaded_file = getUploadedFile();
493:
494: if (m_uploaded_file_isdeleted) {
495: throw new RuntimeException(
496: "Cannot getContentAsString file is deleted already.");
497: }
498:
499: if (uploaded_file == null) {
500: return "";
501: }
502:
503: file_as_string = ReadFileIntoString(uploaded_file);
504:
505: if (delete_tmp_file) {
506: // check the thread *can* delete
507: SecurityManager security = System.getSecurityManager();
508:
509: if (security != null) {
510: security.checkDelete(uploaded_file
511: .getAbsolutePath());
512: }
513:
514: m_uploaded_file_isdeleted = uploaded_file.delete();
515:
516: }
517: }
518:
519: return file_as_string;
520: }
521:
522: /**
523: * Retrieves the body of the request as character data using a <code>BufferedReader</code>
524: * and returns as a <code>String</code>.
525: *
526: * @return
527: * @throws IOException
528: */
529: public String getStringFromReqReader() throws IOException {
530: BufferedReader reqXML = m_nonmulti_request.getReader();
531:
532: StringBuffer m_doc_buff = new StringBuffer();
533:
534: m_doc_buff.append(reqXML.readLine());
535:
536: while (reqXML.ready()) {
537: m_doc_buff.append(reqXML.readLine());
538: }
539:
540: return m_doc_buff.toString();
541: }
542:
543: /**
544: * Returns a <code>String</code> representation of the parameters and their values
545: * of the request.
546: *
547: * @return
548: */
549: public String getParameterDescription() {
550: StringBuffer str_buf = new StringBuffer();
551:
552: Enumeration p_names = getParameterNames();
553:
554: while (p_names.hasMoreElements()) {
555: String p_name = (String) p_names.nextElement();
556: String[] p_values_array = getParameterValues(p_name);
557: Vector p_values = new Vector();
558:
559: for (int i = 0; i < p_values_array.length; i++)
560: p_values.addElement(p_values_array[i]);
561:
562: str_buf.append(p_name).append(" = ").append(
563: p_values.toString()).append(
564: " (" + p_values.size() + ")").append("\n");
565: }
566:
567: return str_buf.toString();
568: }
569:
570: /*---------------------------------------------------------------------------
571:
572: Private methods
573:
574: ---------------------------------------------------------------------------*/
575:
576: /**
577: * Reads the given <code>File</code> to a <code>String</code> and returns it.
578: *
579: * @param source_file
580: * @return
581: * @throws IOException
582: */
583: private static String ReadFileIntoString(File source_file)
584: throws IOException {
585: StringBuffer str_buf = new StringBuffer();
586:
587: // open the file
588: BufferedReader file_reader = new BufferedReader(
589: new InputStreamReader(new FileInputStream(source_file),
590: "UTF-8"));
591:
592: String current_line = "";
593: int current_line_number = 1;
594:
595: try {
596: while ((current_line = file_reader.readLine()) != null) {
597: str_buf.append(current_line);
598: str_buf.append("\n");
599: current_line_number++;
600: }
601: } catch (Exception e) {
602: throw new RuntimeException(
603: "Problem reading temporary uploaded file:"
604: + source_file + " (" + e + ")"
605: + ", offending line number:"
606: + current_line_number + " value:"
607: + current_line);
608: }
609:
610: file_reader.close();
611:
612: return str_buf.toString();
613: }
614:
615: /**
616: * Returns the file associated with <code>savepath</code>.
617: *
618: * @param savepath
619: * @return
620: */
621: private File getFile(String savepath) {
622: if (savepath == null) {
623: throw new IllegalArgumentException("No filename passed:"
624: + savepath);
625: }
626:
627: if (m_is_multipart_request) {
628: return m_multi_request.getFile(savepath);
629: } else {
630: throw new RuntimeException(
631: "Cannot get file, not multipart form data");
632: }
633:
634: }
635:
636: /**
637: * Sets the flag for whether the content is binary or not.
638: *
639: * @param upload_is_binary
640: */
641: private void setIsBinaryUpload(boolean upload_is_binary) {
642: m_upload_is_binary = upload_is_binary;
643: }
644:
645: }
|