com.bristle.javalib.net.http.MultiPartFormDataParamMap.java Source code

Java tutorial

Introduction

Here is the source code for com.bristle.javalib.net.http.MultiPartFormDataParamMap.java

Source

// Copyright (C) 2007-2012 Bristle Software, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 1, or (at your option)
// any later version.
// 
// This program 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 General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc.

package com.bristle.javalib.net.http;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileItemStream.ItemSkippedException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;

// MultiPartFormDataParamMap
/******************************************************************************
* This class makes the form parameters from an HTML form with 
*       enctype='multipart/form-data' 
* available as a simple map, like the map returned by 
* {@link HttpServletRequest#getParameterMap()} 
* for other HTML forms.  It can also be used with regular HTML forms, but it 
* offers no advantages in that case, because it simply returns the map 
* returned by {@link HttpServletRequest#getParameterMap()}.
* <br>
* It handles regular form parameters differently than streamed file 
* parameters (the kind generated by the HTML INPUT tag with TYPE='FILE').
* Regular form parameters are added to the map, and can be accessed at any
* time, in the usual fashion.  However, streamed files are not added to 
* the map.  Instead, they are passed as {@link FileItemStream}s to the 
* specified callback method (if any) as they are discovered during the 
* iteration over the incoming HTTP request stream.  Such files must be fully 
* processed during the callback, because they become unavailable as soon as 
* the iteration resumes.  This is because they are really just part of the 
* larger HTTP request stream.  Attempts to access the {@link FileItemStream} 
* after the callback completes throw the exception:
*       {@link ItemSkippedException}
* 
*<pre>
*<b>Usage:</b>
*
*   - The typical scenario for using this class from Java code is:
*     - Call {@link #parseRequestStream(HttpServletRequest,FileItemStreamCallBack)} 
*       or {@link #parseRequestStream(HttpServletRequest)}, passing an HTTP 
*       request and an optional callback.  This parses the request, and calls 
*       the callback (if any) for each file (if any) in the streamed HTTP 
*       request:
*           import org.apache.commons.fileupload.FileItemStream;
*           class MyCallback 
*                   implements MultiPartFormDataParamMap.FileItemStreamCallBack
*           {
*               public void fullyProcessAFileItemStream
*                                   (FileItemStream fileItemStream) 
*                                    throws Exception
*               {
*                   String strFieldName   = fileItemStream.getFieldName());
*                   String strFileName    = fileItemStream.getName();
*                   String strContentType = fileItemStream.getContentType();
*                   InputStream streamIn  = fileItemStream.openStream();
*                   processTheStreamSavingToDiskOrWhatever(streamIn);
*               }
*           }
*           ...
*           MultiPartFormDataParamMap formParams 
*                               = new MultiPartFormDataParamMap();
*           formParams.parseRequestStream(request, new MyCallback());
*     - Access the param map as you would ordinarily access the map returned
*       by {@link HttpServletRequest#getParameterMap()}:
*           formParams.containsKey("param1")
*           formParams.containsKey(strParam)
*           formParams.get("param1")
*           formParams.get(strParam)
*           etc.
*     - For form parameters that occur earlier in the HTTP request stream 
*       than a file, you can access the formParams map during the callback
*       for that file as well as after it.  For form parameters that occur 
*       later in the stream, the parameters will not yet be in the map.
*
*   - The typical scenario for using this class from JSP code is:
*     - Call {@link #parseRequestStream(HttpServletRequest,FileItemStreamCallBack)} 
*       or {@link #parseRequestStream(HttpServletRequest)} from Java code, 
*       exactly as above.
*     - Add the map to the context for convenient access from EL expressions:
*           <%
*           pageContext.setAttribute("formParams", formParams);
*           %>
*     - Access the non-file params in the formParams map via EL:
*           ${formParams.param1}
*           ${formParams["param1"]}
*           ${formParams[strParam]}
*
*<b>Assumptions:</b>
*<b>Effects:</b>
*       - None.
*<b>Anticipated Changes:</b>
*<b>Notes:</b>
*<b>Implementation Notes:</b>
*<b>Portability Issues:</b>
*<b>Revision History:</b>
*   $Log$
*</pre>
******************************************************************************/
public class MultiPartFormDataParamMap extends HashMap {
    //
    // Class variables
    //

    //
    // Instance variables to support public properties
    //

    //
    // Internal instance variables
    //

    /**************************************************************************
     * This number identifies the version of the class definition, used for 
     * serialized instances.  Be sure to increment it when adding/modifying
     * instance variable definitions or making any other change to the class
     * definition.  Omitting this declaration causes a compiler warning for 
     * any class that implements java.io.Serializable.
     *************************************************************************/
    private static final long serialVersionUID = 1L;

    /**************************************************************************
    * This interface must be implemented by any class that expects to be
    * called as a callback by {@link #parseRequestStream(HttpServletRequest,
    * FileItemStreamCallBack)}.
    **************************************************************************/
    public interface FileItemStreamCallBack {
        /**********************************************************************
        * Fully process the specified FileItemStream, without keeping a 
        * reference to it.
        *@param  fileItemStream The FileItemStream to fully process.
        *@throws Throwable  Permitted to throw any Throwable.  
        *                   It will be propagated to the caller of 
        *                   {@link #parseRequestStream(HttpServletRequest,
        *                   FileItemStreamCallBack)}.
        **********************************************************************/
        public void fullyProcessAFileItemStream(FileItemStream fileItemStream) throws Throwable;
    }

    /**************************************************************************
    * Parse the specified HTTP request, initializing the map, and calling 
    * the specified callback (if not null) for each file (if any) in the 
    * streamed HTTP request. 
    *
    *@param request              The HTTP request
    *@param callback             The callback class
    *@throws FileUploadException When the request is badly formed.
    *@throws IOException         When an I/O error occurs reading the request.
    *@throws Throwable           When thrown by the callback.
    **************************************************************************/
    public void parseRequestStream(HttpServletRequest request, FileItemStreamCallBack callback)
            throws FileUploadException, IOException, Throwable {
        if (ServletFileUpload.isMultipartContent(request)) {
            ServletFileUpload upload = new ServletFileUpload();
            FileItemIterator iter = upload.getItemIterator(request);
            while (iter.hasNext()) {
                FileItemStream fileItemStream = iter.next();
                if (fileItemStream.isFormField()) {
                    String strParamName = fileItemStream.getFieldName();
                    InputStream streamIn = fileItemStream.openStream();
                    String strParamValue = Streams.asString(streamIn);
                    put(strParamName, strParamValue);
                    // Note: Can't do the following usefully.  The Parameter 
                    //       Map of the HTTP Request is effectively readonly.
                    //       This does not report an error, but is a no-op.
                    // request.getParameterMap().put(strParamName, strParamValue);
                } else {
                    if (callback != null) {
                        callback.fullyProcessAFileItemStream(fileItemStream);
                    }
                }
            }
        } else {
            putAll(request.getParameterMap());
        }
    }

    /**************************************************************************
    * Parse the specified HTTP request, initializing the map.
    *
    *@param request              The HTTP request
    *@throws FileUploadException When the request is badly formed.
    *@throws IOException         When an I/O error occurs reading the request.
    **************************************************************************/
    public void parseRequestStream(HttpServletRequest request) throws FileUploadException, IOException {
        try {
            parseRequestStream(request, null);
        } catch (FileUploadException exception) {
            throw exception;
        } catch (IOException exception) {
            throw exception;
        } catch (Throwable exception) {
            // Catch Throwable here to avoid having to declare it in the 
            // the throws clause.  It is declared in:
            //      parseRequestStream(request, callback)
            // because the callback may throw it, and we want to propagate
            // it to the caller.  But, in this case, there is no callback,
            // so it is hard to justify declaring Throwable in the throws
            // clause.
            //?? Not ideal because now we are catching Throwable, in the
            //?? unlikely case that an undeclared exception occurs.  How to 
            //?? not catch it, but not have to declare it?
        }
    }
}