KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > uihandler > MultiPartHandler


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package org.netbeans.lib.uihandler;
25 /*
26  * MultipartHandler:
27  * A utility class to handle content of multipart/form-data type used in form uploads.
28  *
29  * Parses and provides accessor functions to extract the form fields and the uploaded
30  * file content parts separated by a boundary string.
31  * See http://www.ietf.org/rfc/rfc1867.txt.
32  */

33
34 import java.io.BufferedOutputStream JavaDoc;
35 import java.io.ByteArrayOutputStream JavaDoc;
36 import java.io.File JavaDoc;
37 import java.io.FileOutputStream JavaDoc;
38 import java.io.FilterInputStream JavaDoc;
39 import java.io.IOException JavaDoc;
40 import java.io.InputStream JavaDoc;
41 import java.io.OutputStream JavaDoc;
42 import java.util.Enumeration JavaDoc;
43 import java.util.Hashtable JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.Vector JavaDoc;
46 import java.util.zip.GZIPInputStream JavaDoc;
47
48 public class MultiPartHandler {
49   public interface InputFacade {
50       public int readLine(byte[] arr, int off, int len) throws IOException JavaDoc;
51       public InputStream JavaDoc getInputStream();
52   }
53   
54   public interface RequestFacade {
55       public int getContentLength();
56       public String JavaDoc getContentType();
57       public InputFacade getInput() throws IOException JavaDoc;
58   }
59
60   private static final int DEFAULT_MAX_UPLOAD_SIZE = 1024 * 1024; // 1Mb
61

62   protected Hashtable JavaDoc<String JavaDoc,Vector JavaDoc<String JavaDoc>> formFields = new Hashtable JavaDoc<String JavaDoc,Vector JavaDoc<String JavaDoc>>();
63   protected Hashtable JavaDoc<String JavaDoc,OneUpload> uploadFiles = new Hashtable JavaDoc<String JavaDoc,OneUpload>();
64
65   /** servlet request */
66   private RequestFacade req;
67
68   /** input stream to read parts from */
69   private InputFacade in;
70   
71   /** MIME boundary that delimits parts */
72   private String JavaDoc boundary;
73   
74   /** buffer for readLine method */
75   private byte[] buf = new byte[8 * 1024];
76   
77   /** upload directory */
78   private File JavaDoc uploadDir;
79
80   /** encoding used for the from fields */
81   private String JavaDoc fieldEncoding = "ISO-8859-1";
82
83   // i18n StringManager
84
/*private static StringManager localStrings =
85     StringManager.getManager( OneUpload.class );*/

86
87   /**
88    * Instantiate a new multipart handler with default
89    */

90   public MultiPartHandler(RequestFacade request,
91                           String JavaDoc tmpDirectory) throws IOException JavaDoc {
92     this(request, tmpDirectory, DEFAULT_MAX_UPLOAD_SIZE, "ISO-8859-1");
93   }
94
95   public MultiPartHandler(RequestFacade request,
96                           String JavaDoc tmpDirectory,
97                           int maxUploadSize) throws IOException JavaDoc {
98     this(request, tmpDirectory, maxUploadSize, "ISO-8859-1");
99   }
100
101   /**
102    * Instantiate a new OneUpload to handle the given request,
103    * saving any uploaded files to the given directory and limiting the
104    * upload size to maxUploadSize.
105    *
106    * An IOException is thrown when the request content-type doesn't match
107    * with multipart/form-data or if the upload size exceeds the given limit.
108    *
109    * call parseMultipartUpload() to parse various parts of the posted data and then
110    * call getParameter(), getParameters(), getParameterNames() and getParameterValues()
111    * functions to access form field names and their values.
112    * call getFile(), getFileType() to access uploaded file and its content-type.
113    */

114   public MultiPartHandler(RequestFacade request,
115                           String JavaDoc tmpDirectory,
116                           int maxUploadSize,
117                           String JavaDoc fieldEncoding) throws IOException JavaDoc {
118
119     // Ensure we are passed legal arguments
120
if (request == null) {
121       //String msg = localStrings.getString( "admin.server.gui.servlet.request_cannot_be_null" );
122
throw new IllegalArgumentException JavaDoc( "request is null" );
123     }
124     if (tmpDirectory == null) {
125       //String msg = localStrings.getString( "admin.server.gui.servlet.tmpdirectory_cannot_be_null" );
126
throw new IllegalArgumentException JavaDoc( "tmp Dir is null" );
127     }
128     if (maxUploadSize <= 0) {
129         //String msg = localStrings.getString( "admin.server.gui.servlet.maxpostsize_must_be_positive" );
130
throw new IllegalArgumentException JavaDoc( "Max size is < 0" );
131     }
132
133     // Ensure that the directory exists and is writable (this should be a temp directory)
134
uploadDir = new File JavaDoc(tmpDirectory);
135     if (!uploadDir.isDirectory()) {
136       //String msg = localStrings.getString( "admin.server.gui.servlet.not_directory", tmpDirectory );
137
throw new IllegalArgumentException JavaDoc( "Not a Directory" );
138     }
139     if (!uploadDir.canWrite()) {
140       //String msg = localStrings.getString( "admin.server.gui.servlet.not_writable", tmpDirectory );
141
throw new IllegalArgumentException JavaDoc("write protected" );
142     }
143     int length = request.getContentLength();
144     //commented this code to remove the restriction on the file upload size.
145
/*if (length > maxUploadSize) {
146       //String msg = localStrings.getString( "admin.server.gui.servlet.posted_content_length_exceeds_limit", new Integer(length), new Integer(maxUploadSize) );
147       throw new IOException( msg );
148     }*/

149     // Check the content type to make sure it's "multipart/form-data"
150
String JavaDoc type = request.getContentType();
151     if (type == null ||
152         !type.toLowerCase().startsWith("multipart/form-data")) {
153       //String msg = localStrings.getString( "admin.server.gui.servlet.posted_content_type_not_multipart" );
154
throw new IOException JavaDoc( "type null" );
155     }
156
157     // Check the content length to prevent denial of service attacks
158
this.fieldEncoding = fieldEncoding;
159     this.req = request;
160   }
161
162   /* parseMultipartUpload:
163    *
164    * This function parses the multipart/form-data and throws an IOException
165    * if there's any problem reading or parsing the request or if the posted
166    * content is larger than the maximum permissible size.
167    */

168   public void parseMultipartUpload()
169                 throws IOException JavaDoc {
170     // setup the initial buffered input stream, boundary string that separates
171
// various parts in the stream.
172
startMultipartParse();
173
174     HashMap JavaDoc partHeaders = parsePartHeaders();
175     while (partHeaders != null) {
176
177         String JavaDoc fieldName = (String JavaDoc)partHeaders.get("fieldName");
178         String JavaDoc fileName = (String JavaDoc)partHeaders.get("fileName");
179
180         if (fileName != null) {
181             // This is a file upload part
182
if (fileName.equals("")) {
183                 fileName = null; // empty filename, probably an "empty" file param
184
}
185
186             if (fileName != null) {
187                 // a filename was actually specified
188
String JavaDoc content = (String JavaDoc)partHeaders.get("content-type");
189                 fileName = saveUploadFile(fileName, content);
190
191                 uploadFiles.put(fieldName,
192                     new OneUpload(
193                        uploadDir.toString(),
194                        fileName,
195                        content
196                    )
197                 );
198             }
199             else {
200         // FIXME: This does not call saveUploadFile(name);
201
uploadFiles.put(fieldName, new OneUpload(null, null, null));
202             }
203         }
204         else {
205             // this is a parameters list part
206
byte[] valueBytes = parseFormFieldBytes();
207             String JavaDoc value = new String JavaDoc(valueBytes, fieldEncoding);
208
209             Vector JavaDoc<String JavaDoc> existingValues = formFields.get(fieldName);
210             if (existingValues == null) {
211                 existingValues = new Vector JavaDoc<String JavaDoc>();
212                 formFields.put(fieldName, existingValues);
213             }
214             existingValues.addElement(value);
215         }
216
217         partHeaders.clear();
218         partHeaders = parsePartHeaders();
219     }
220   }
221
222   private void startMultipartParse()
223                 throws IOException JavaDoc {
224     // Get the boundary string; it's included in the content type.
225
// Should look something like "------------------------12012133613061"
226
String JavaDoc boundary = parseBoundary(req.getContentType());
227     if (boundary == null) {
228       //String msg = localStrings.getString( "admin.server.gui.servlet.separation_boundary_not_specified" );
229
throw new IOException JavaDoc( "boundary is nul" );
230     }
231
232     this.in = req.getInput();
233     this.boundary = boundary;
234     
235     // Read the first line, should be the first boundary
236
String JavaDoc line = readLine();
237     if (line == null) {
238       //String msg = localStrings.getString( "admin.server.gui.servlet.corrupt_form_data_premature_ending" );
239
throw new IOException JavaDoc( "line is null" );
240     }
241
242     // Verify that the line is the boundary
243
if (!line.startsWith(boundary)) {
244       //String msg = localStrings.getString( "admin.server.gui.servlet.corrupt_form_data_no_leading_boundary", line, boundary );
245
throw new IOException JavaDoc( "not start with boundary" );
246     }
247   }
248
249   /**
250    * parse the headers of the individual part; they look like this:
251    * Content-Disposition: form-data; name="field1"; filename="file1.txt"
252    * Content-Type: type/subtype
253    * Content-Transfer-Encoding: binary
254    */

255   private HashMap JavaDoc parsePartHeaders() throws IOException JavaDoc {
256     HashMap JavaDoc<String JavaDoc,String JavaDoc> partHeaders = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
257
258     Vector JavaDoc<String JavaDoc> headers = new Vector JavaDoc<String JavaDoc>();
259     String JavaDoc line = readLine();
260     if (line == null) {
261       // No parts left, we're done
262
return null;
263     }
264     else if (line.length() == 0) {
265       // IE4 on Mac sends an empty line at the end; treat that as the end.
266
return null;
267     }
268     headers.addElement(line);
269
270     // Read the following header lines we hit an empty line
271
while ((line = readLine()) != null && (line.length() > 0)) {
272       headers.addElement(line);
273     }
274
275     // If we got a null above, it's the end
276
if (line == null) {
277       return null;
278     }
279
280     // default part content type (rfc1867)
281
partHeaders.put("content-type", "text/plain");
282
283     Enumeration JavaDoc ee = headers.elements();
284     while (ee.hasMoreElements()) {
285       String JavaDoc headerline = (String JavaDoc) ee.nextElement();
286
287       if (headerline.toLowerCase().startsWith("content-disposition:")) {
288         // Parse the content-disposition line
289
parseContentDisposition(headerline, partHeaders);
290       }
291       else if (headerline.toLowerCase().startsWith("content-type:")) {
292         // Get the content type, or null if none specified
293
parseContentType(headerline, partHeaders);
294       }
295     }
296
297     return partHeaders;
298   }
299
300   /**
301    * parses and returns the boundary token from a line.
302    */

303   private String JavaDoc parseBoundary(String JavaDoc line) {
304     // Use lastIndexOf() because IE 4.01 on Win98 has been known to send the
305
// "boundary=" string multiple times.
306
int index = line.lastIndexOf("boundary=");
307     if (index == -1) {
308       return null;
309     }
310     String JavaDoc boundary = line.substring(index + 9); // 9 for "boundary="
311
if (boundary.charAt(0) == '"') {
312       // The boundary is enclosed in quotes, strip them
313
index = boundary.lastIndexOf('"');
314       boundary = boundary.substring(1, index);
315     }
316
317     // The real boundary is always preceeded by an extra "--"
318
boundary = "--" + boundary;
319
320     return boundary;
321   }
322
323   /**
324    * parses and returns content-disposition header and stores the values
325    * in the partHeaders.
326    *
327    * throws IOException if the line is malformatted.
328    */

329   private void parseContentDisposition(String JavaDoc line, HashMap JavaDoc<String JavaDoc,String JavaDoc> partHeaders)
330                     throws IOException JavaDoc {
331
332     // Convert the line to a lowercase string without the ending \r\n
333
// Keep the original line for error messages and for variable names.
334
String JavaDoc origline = line;
335     line = origline.toLowerCase();
336
337     // Get the content disposition, should be "form-data"
338
int start = line.indexOf("content-disposition: ");
339     int end = line.indexOf(";");
340     if (start == -1 || end == -1) {
341       //String msg = localStrings.getString( "admin.server.gui.servlet.content_disposition_corrupt", origline );
342
throw new IOException JavaDoc( "end reached" );
343     }
344     String JavaDoc disposition = line.substring(start + 21, end);
345     if (!disposition.equals("form-data")) {
346       //String msg = localStrings.getString( "admin.server.gui.servlet.invalid_content_disposition", disposition );
347
throw new IOException JavaDoc( "fome-data not match" );
348     }
349
350     // Get the field name
351
start = line.indexOf("name=\"", end); // start at last semicolon
352
end = line.indexOf("\"", start + 7); // skip name=\"
353
if (start == -1 || end == -1) {
354       //String msg = localStrings.getString( "admin.server.gui.servlet.content_disposition_corrupt", origline );
355
throw new IOException JavaDoc( "data corrupt" );
356     }
357
358     String JavaDoc name = origline.substring(start + 6, end);
359
360     // Get the fileName, if given
361
String JavaDoc fileName = null;
362     String JavaDoc origFileName = null;
363     start = line.indexOf("filename=\"", end + 2); // start after name
364
end = line.indexOf("\"", start + 10); // skip filename=\"
365

366     if (start != -1 && end != -1) { // note the !=
367
fileName = origline.substring(start + 10, end);
368       origFileName = fileName;
369       // The filename may contain a full path. Cut to just the filename.
370
int slash =
371         Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
372       if (slash > -1) {
373         fileName = fileName.substring(slash + 1); // past last slash
374
}
375     }
376
377     // fill in the part parameters map: disposition, name, filename
378
// empty fileName denotes no file posted!
379
partHeaders.put("disposition", disposition);
380     partHeaders.put("fieldName", name);
381     partHeaders.put("fileName", fileName);
382     partHeaders.put("filePath", origFileName);
383   }
384
385   /**
386    * parse and returns the content type from a line, or null if the
387    * line was empty.
388    */

389   private void parseContentType(String JavaDoc line, HashMap JavaDoc<String JavaDoc,String JavaDoc> partHeaders)
390                 throws IOException JavaDoc {
391     String JavaDoc contentType = null;
392
393     // Convert the line to a lowercase string
394
String JavaDoc origline = line;
395     line = origline.toLowerCase();
396
397     // Get the content type, if any
398
if (line.startsWith("content-type")) {
399       int start = line.indexOf(" ");
400
401       if (start == -1) {
402         //String msg = localStrings.getString( "admin.server.gui.servlet.corrupt_content_type", origline );
403
throw new IOException JavaDoc( "no start" );
404       }
405       contentType = line.substring(start + 1);
406       
407       partHeaders.put("content-type", contentType);
408     }
409     else if (line.length() != 0) { // no content type, so should be empty
410
//String msg = localStrings.getString( "admin.server.gui.servlet.malformed_line_after_disposition", origline );
411
throw new IOException JavaDoc( "length 0" );
412     }
413   }
414
415  /** parse contents of a form field parameter; uses the encoding set by the user
416   */

417  private byte[] parseFormFieldBytes() throws IOException JavaDoc {
418
419     // Copy the part's contents into a byte array
420
MultipartInputStream pis = new MultipartInputStream(in, boundary);
421
422     ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc(512);
423     byte[] buf = new byte[128];
424     int read;
425     while ((read = pis.read(buf)) != -1) {
426       baos.write(buf, 0, read);
427     }
428     pis.close();
429     baos.close();
430     
431     // get the value bytes
432
return baos.toByteArray();
433   }
434
435   /**
436    * Read the next line of input.
437    *
438    * @return a String containing the next line of input from the stream,
439    * or null to indicate the end of the stream.
440    * @exception IOException if an input or output exception has occurred.
441    */

442   private String JavaDoc readLine() throws IOException JavaDoc {
443     StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
444     int result;
445     String JavaDoc line;
446
447     do {
448       result = in.readLine(buf, 0, buf.length); // does +=
449
if (result != -1) {
450         sbuf.append(new String JavaDoc(buf, 0, result, "ISO-8859-1"));
451       }
452     } while (result == buf.length); // loop only if the buffer was filled
453

454     if (sbuf.length() == 0) {
455       return null; // nothing read, must be at the end of stream
456
}
457
458     // Cut off the trailing \n or \r\n
459
// It should always be \r\n but IE5 sometimes does just \n
460
int len = sbuf.length();
461     if (len >= 2 && sbuf.charAt(len - 2) == '\r') {
462       sbuf.setLength(len - 2); // cut \r\n
463
}
464     else {
465       sbuf.setLength(len - 1); // cut \n
466
}
467     return sbuf.toString();
468   }
469
470   /**
471    * Write this file part to the specified directory.
472    */

473   private String JavaDoc saveUploadFile(String JavaDoc fileName, String JavaDoc content)
474                 throws IOException JavaDoc {
475
476     long written = 0;
477     OutputStream JavaDoc fileOut = null;
478
479     try {
480       // Only do something if this part contains a file
481
File JavaDoc file = new File JavaDoc(uploadDir, fileName);
482       for (int i = 0; file.exists(); i++) {
483           if (!file.exists()) {
484               break;
485           }
486           file = new File JavaDoc(uploadDir, fileName + "." + i);
487       }
488       fileName = file.getName();
489
490       fileOut = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(file));
491       int numBytes;
492       byte[] buf = new byte[8 * 1024];
493
494       InputStream JavaDoc partInput;
495       if (content.equals("x-application/gzip")) { // NOI18N
496
// sending from NetBeans UI Gestures Collector
497
partInput = new GZIPInputStream JavaDoc(in.getInputStream());
498       } else {
499           /** input stream containing file data */
500           partInput = new MultipartInputStream(in, boundary);
501       }
502       while((numBytes = partInput.read(buf)) != -1) {
503         fileOut.write(buf, 0, numBytes);
504         written += numBytes;
505       }
506       partInput.close();
507     }
508     finally {
509       if (fileOut != null) fileOut.close();
510     }
511
512     return fileName;
513   }
514
515
516   /**
517    * Returns the names of all the parameters as an Enumeration of
518    * Strings. It returns an empty Enumeration if there are no parameters.
519    *
520    */

521   public Enumeration JavaDoc getParameterNames() {
522     return formFields.keys();
523   }
524
525   /**
526    * Returns the names of all the uploaded files as an Enumeration of
527    * Strings. It returns an empty Enumeration if there are no uploaded
528    * files. Each file name is the name specified by the form, not by
529    * the user.
530    *
531    */

532   public Enumeration JavaDoc getFileNames() {
533     return uploadFiles.keys();
534   }
535
536   /**
537    * Returns the value of the named parameter as a String, or null if
538    * the parameter was not sent or was sent without a value. The value
539    * is guaranteed to be in its normal, decoded form. If the parameter
540    * has multiple values, only the last one is returned (for backward
541    * compatibility). For parameters with multiple values, it's possible
542    * the last "value" may be null.
543    *
544    */

545   public String JavaDoc getParameter(String JavaDoc name) {
546     try {
547       Vector JavaDoc values = (Vector JavaDoc)formFields.get(name);
548       if (values == null || values.size() == 0) {
549         return null;
550       }
551       String JavaDoc value = (String JavaDoc)values.elementAt(values.size() - 1);
552       return value;
553     }
554     catch (Exception JavaDoc e) {
555       return null;
556     }
557   }
558
559   /**
560    * Returns the values of the named parameter as a String array, or null if
561    * the parameter was not sent. The array has one entry for each parameter
562    * field sent. If any field was sent without a value that entry is stored
563    * in the array as a null. The values are guaranteed to be in their
564    * normal, decoded form. A single value is returned as a one-element array.
565    *
566    */

567   public String JavaDoc[] getParameterValues(String JavaDoc name) {
568     try {
569       Vector JavaDoc values = (Vector JavaDoc)formFields.get(name);
570       if (values == null || values.size() == 0) {
571         return null;
572       }
573       String JavaDoc[] valuesArray = new String JavaDoc[values.size()];
574       values.copyInto(valuesArray);
575       return valuesArray;
576     }
577     catch (Exception JavaDoc e) {
578       return null;
579     }
580   }
581
582   /**
583    * Returns the filesystem name of the specified file, or null if the
584    * file was not included in the upload. A filesystem name is the name
585    * specified by the user. It is also the name under which the file is
586    * actually saved.
587    *
588    */

589   public String JavaDoc getFileName(String JavaDoc name) {
590     try {
591       OneUpload file = uploadFiles.get(name);
592       return file.getFileName(); // may be null
593
}
594     catch (Exception JavaDoc e) {
595       return null;
596     }
597   }
598
599   /**
600    * Returns the content type of the specified file (as supplied by the
601    * client browser), or null if the file was not included in the upload.
602    *
603    */

604   public String JavaDoc getFileType(String JavaDoc name) {
605     try {
606       OneUpload file = uploadFiles.get(name);
607       return file.getFileType(); // may be null
608
}
609     catch (Exception JavaDoc e) {
610       return null;
611     }
612   }
613
614   /**
615    * Returns a File object for the specified file saved on the server's
616    * filesystem, or null if the file was not included in the upload.
617    *
618    */

619   public File JavaDoc getFile(String JavaDoc name) {
620     try {
621       OneUpload file = uploadFiles.get(name);
622       return file.getFile(); // may be null
623
}
624     catch (Exception JavaDoc e) {
625       return null;
626     }
627   }
628
629   /**
630    * close the multi-part form handler
631    */

632   public void close() throws IOException JavaDoc {
633     req = null;
634     in = null;
635     boundary = null;
636     buf = null;
637     uploadDir = null;
638   }
639   
640
641   /** A class to hold information about an uploaded file. */
642   private static class OneUpload {
643       
644       private String JavaDoc dir;
645       private String JavaDoc filename;
646       private String JavaDoc type;
647       
648       OneUpload(String JavaDoc dir, String JavaDoc filename, String JavaDoc type) {
649           this.dir = dir;
650           this.filename = filename;
651           this.type = type;
652       }
653       
654       public String JavaDoc getFileType() {
655           return type;
656       }
657       
658       public String JavaDoc getFileName() {
659           return filename;
660       }
661       
662       public File JavaDoc getFile() {
663           if (dir == null || filename == null) {
664               return null;
665           } else {
666               return new File JavaDoc(dir + File.separator + filename);
667           }
668       }
669   }
670
671 /*
672  * providing access to a single MIME part contained with in which ends with
673  * the boundary specified. It uses buffering to provide maximum performance.
674  *
675  */

676   private static class MultipartInputStream extends FilterInputStream JavaDoc {
677       /** boundary which "ends" the stream */
678       private String JavaDoc boundary;
679       
680       /** our buffer */
681       private byte [] buf = new byte[64*1024]; // 64k
682

683       /** number of bytes we've read into the buffer */
684       private int count;
685       
686       /** current position in the buffer */
687       private int pos;
688       
689       /** flag that indicates if we have encountered the boundary */
690       private boolean eof;
691       
692       /** associated facade */
693       private MultiPartHandler.InputFacade facade;
694       
695       // i18n StringManager
696
/*private static StringManager localStrings =
697         StringManager.getManager( MultipartInputStream.class );*/

698       
699       /**
700        * Instantiate a MultipartInputStream which stops at the specified
701        * boundary from an underlying ServletInputStream.
702        *
703        */

704       MultipartInputStream(MultiPartHandler.InputFacade in,
705           String JavaDoc boundary) throws IOException JavaDoc {
706           super(in.getInputStream());
707           this.boundary = boundary;
708           this.facade = in;
709       }
710       
711       /**
712        * Fill up our buffer from the underlying input stream, and check for the
713        * boundary that signifies end-of-file. Users of this method must ensure
714        * that they leave exactly 2 characters in the buffer before calling this
715        * method (except the first time), so that we may only use these characters
716        * if a boundary is not found in the first line read.
717        *
718        * @exception IOException if an I/O error occurs.
719        */

720       private void fill() throws IOException JavaDoc
721 {
722           if (eof)
723               return;
724           
725           // as long as we are not just starting up
726
if (count > 0)
727 {
728               // if the caller left the requisite amount spare in the buffer
729
if (count - pos == 2) {
730                   // copy it back to the start of the buffer
731
System.arraycopy(buf, pos, buf, 0, count - pos);
732                   count -= pos;
733                   pos = 0;
734               } else {
735                   // should never happen, but just in case
736
//String msg = localStrings.getString( "admin.server.gui.servlet.fill_detected_illegal_buffer_state" );
737
throw new IllegalStateException JavaDoc( "should never happen" );
738               }
739           }
740           
741           // try and fill the entire buffer, starting at count, line by line
742
// but never read so close to the end that we might split a boundary
743
int read = 0;
744           int maxRead = buf.length - boundary.length();
745           while (count < maxRead) {
746               // read a line
747
read = facade.readLine(buf, count, buf.length - count);
748               // check for eof and boundary
749
if (read == -1) {
750                   //String msg = localStrings.getString( "admin.server.gui.servlet.unexpected_end_part" );
751
throw new IOException JavaDoc( "read is -1" );
752               } else {
753                   if (read >= boundary.length()) {
754                       eof = true;
755                       for (int i=0; i < boundary.length(); i++) {
756                           if (boundary.charAt(i) != buf[count + i]) {
757                               // Not the boundary!
758
eof = false;
759                               break;
760                           }
761                       }
762                       if (eof) {
763                           break;
764                       }
765                   }
766               }
767               // success
768
count += read;
769           }
770       }
771       
772       /**
773        * See the general contract of the read method of InputStream.
774        * Returns -1 (end of file) when the MIME boundary of this part is encountered.
775        *
776        * throws IOException if an I/O error occurs.
777        */

778       public int read() throws IOException JavaDoc {
779           if (count - pos <= 2) {
780               fill();
781               if (count - pos <= 2) {
782                   return -1;
783               }
784           }
785           return buf[pos++] & 0xff;
786       }
787       
788       /**
789        * See the general contract of the read method of InputStream.
790        *
791        * Returns -1 (end of file) when the MIME boundary of this part
792        * is encountered.
793        *
794        * throws IOException if an I/O error occurs.
795        */

796       public int read(byte b[]) throws IOException JavaDoc {
797           return read(b, 0, b.length);
798       }
799       
800       /**
801        * See the general contract of the read method of InputStream.
802        *
803        * Returns -1 (end of file) when the MIME boundary of this part is encountered.
804        *
805        * throws IOException if an I/O error occurs.
806        */

807       public int read(byte b[], int off, int len) throws IOException JavaDoc
808 {
809           int total = 0;
810           if (len == 0) {
811               return 0;
812           }
813           
814           int avail = count - pos - 2;
815           if (avail <= 0) {
816               fill();
817               avail = count - pos - 2;
818               if(avail <= 0) {
819                   return -1;
820               }
821           }
822           int copy = Math.min(len, avail);
823           System.arraycopy(buf, pos, b, off, copy);
824           pos += copy;
825           total += copy;
826           
827           while (total < len) {
828               fill();
829               avail = count - pos - 2;
830               if(avail <= 0) {
831                   return total;
832               }
833               copy = Math.min(len - total, avail);
834               System.arraycopy(buf, pos, b, off + total, copy);
835               pos += copy;
836               total += copy;
837           }
838           return total;
839       }
840       
841       /**
842        * Returns the number of bytes that can be read from this input stream
843        * without blocking. This is a standard InputStream idiom
844        * to deal with buffering gracefully, and is not same as the length of the
845        * part arriving in this stream.
846        *
847        * throws IOException if an I/O error occurs.
848        */

849       public int available() throws IOException JavaDoc {
850           int avail = (count - pos - 2) + in.available();
851           // Never return a negative value
852
return (avail < 0 ? 0 : avail);
853       }
854       
855       /**
856        * Closes this input stream and releases any system resources
857        * associated with the stream. This method will read any unread data
858        * in the MIME part so that the next part starts an an expected place in
859        * the parent InputStream.
860        *
861        * throws IOException if an I/O error occurs.
862        */

863       public void close() throws IOException JavaDoc {
864           if (!eof) {
865               while (read(buf, 0, buf.length) != -1)
866                   ; // do nothing
867
}
868       }
869   }
870   
871 }
872
873
Popular Tags