private FormData readFormData_multiPartFormData( String param_contentType,
						     HeaderParams headerParams ) 
	throws IOException,
	       HeaderFormatException,
	       DataFormatException,
	       UnsupportedFormatException {


	// The content type header should look like something like this:
	// Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeANQMKoBwsmwQrYZ


	try {
	    // System.out.println( "xxxxxxxxx" + getClass().getName() + ": params=" + headerParams );
	    /*String boundary = headerParams.getTokenByPrefix( 0,           // on first level
							     "boundary=", // the token name - so to say
							     false        // not case sensitive?
							     );  
	    */
	    String boundary = headerParams.getTokenValue( param_contentType, // "multipart/form-data",           // on first level
							  "boundary", // the token name - so to say
							  false,        // not case sensitive?
							  1             // get first token ('boundary' is at index 0)
							  );

	    //KeyValueStringPair kv_boundary = KeyValueStringPair.split( boundary );

	    if( boundary == null 
		|| boundary.length() == 0
		//|| kv_boundary == null
		//|| kv_boundary.getValue() == null
		//|| kv_boundary.getValue().length() == 0 
		) {

		 this.getLogger().log( Level.INFO,
				   getClass().getName() + ".readFormData_multiPartFormData()",
				   "Cannot split multi part form data. Required 'boundary' not present or empty in header field 'Content-Type'." );
		 throw new HeaderFormatException( "Cannot split multi part form data. Required 'boundary' not present or empty in header field 'Content-Type'." );

	    }


	    // Explode boundary (later: use a common parser class here for parsing header lines containing
	    // tokens with '=').
	    //String boundary_value          = kv_boundary.getValue();

	    
	    this.getLogger().log( Level.INFO,
				  getClass().getName() + ".readFormData_multiPartFormData()",
				  "Processing POST data using boundary '" + boundary + "' ..." );

	    // The format of the multipart-body is:
	    // ------------------------------------
	    //  boundary        := 0*69<bchars> bcharsnospace
	    //  bchars          := bcharsnospace / " "
	    //  bcharsnospace   := DIGIT / ALPHA / "'" / "(" / ")" /
	    //                     "+" / "_" / "," / "-" / "." /
	    //                     "/" / ":" / "=" / "?"
	    //
	    // --- with --------------------------
	    //
	    //  multipart-body  := [preamble CRLF]
            //                     dash-boundary transport-padding CRLF
            //                     body-part *encapsulation
            //                     close-delimiter transport-padding
            //                     [CRLF epilogue]
	    //  dash-boundary   := "--" boundary
	    //  encapsulation   := delimiter transport-padding
	    //                     CRLF body-part
	    //  delimiter       := CRLF dash-boundary
	    //  close-delimiter := delimiter "--"
	    //
	    // --- Example: ----------------------
	    //  --boundary
	    //  1. body-part
	    //  --boundary
	    //  2. body-part
	    //  --boundary
	    //  3. body-part
	    //  --boundary--
	    //
	    // (See http://stackoverflow.com/questions/4656287/what-rules-apply-to-mime-boundary)

	    // So to detect if the last boundary is reached we need to check if the next
	    // body-party begins with 
	    // '--' CRLF
	    //
	    // This can only be done using a pushback input stream.
	    //
	    
	    MultipartMIMETokenizer mmt = new MultipartMIMETokenizer( this.getInputStream(),   
								     boundary   // Use the raw boundary from the headers here!
								     );
	    ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream( 128 );
	    FormData formData = new DefaultFormData( boundary );


	    InputStream token;
	    int  tokenIndex  = 0;
	    long tokenLength = 0;
	    long totalLength = 0;
	    byte[] endBuffer = new byte[4];
	    while( (token = mmt.getNextToken()) != null ) {

		this.getLogger().log( Level.INFO,
				      getClass().getName() + ".readFormData_multiPartFormData()",
				      "Processing multiform part " + tokenIndex + " by reading from token/input: " + token.toString() + ", globalInputStream=" + this.getInputStream() );



		ByteArrayOutputStream bufferOut = new ByteArrayOutputStream( 256 );
		CustomUtil.transfer( token, 
				     bufferOut, 
				     -1,         // no maxReadLength
				     128         // bufferSize
				     );
		byte[] byteArray = bufferOut.toByteArray();

		// Don't forget that each part itself has a trailing CRLF inside the POST data!
		// See http://www.ietf.org/rfc/rfc2046.txt for details.
		ByteArrayInputStream bufferIn   = null;
		if( byteArray.length >= 2 
		    && byteArray[byteArray.length-2] == Constants.CR
		    && byteArray[byteArray.length-1] == Constants.LF ) {
		    
		    // Drop trailing CRLF
		    bufferIn = new ByteArrayInputStream( byteArray, 0, byteArray.length-2 );

		} else {

		    // Ooops ... the specification tells that there should be a trailing CRLF, but there isn't.
		    // --> Don't interrupt; print warning and continue with full data block
		    this.getLogger().log( Level.WARNING,
					  getClass().getName() + ".readFormData_multiPartFormData()",
					  "Multiform part " + tokenIndex + " misses the trailing CRLF bytes (not found). Continuing though with full data block. Further data processing might probably fail!" );
		    bufferIn = new ByteArrayInputStream( byteArray );

		}

		/*int b;
		tokenLength = 0;
		while( (b = token.read()) != - 1 )  {

		    System.out.print( (char)b );
		    tokenLength++;
		    totalLength++;
		    }*/
		

		HTTPHeaders itemHeaders = HTTPHeaders.read( bufferIn ); // token );
		FormDataItem item       = new FormDataItem( itemHeaders, bufferIn ); // token );
		formData.add( item );


		this.getLogger().log( Level.INFO,
				      getClass().getName() + ".readFormData_multiPartFormData()",
				      "Processed multiform part " + tokenIndex + ": " + tokenLength + " bytes." );
		
		tokenIndex++;
	    }
	    

	    // It is not necesary to consume trailing bytes here; the HTTPRequestDistributor will
	    // do so later (IF there are more bytes available at all).

	    

	    return formData; 

	} catch( IOException e ) {

	     this.getLogger().log( Level.INFO,
				   getClass().getName() + ".readFormData_multiPartFormData()",
				   "[IOException] Failed to read/split multi part form data: " + e.getMessage() );
	     throw e;

	}

    }