View Javadoc

1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.14 2004/04/18 23:51:37 jsdever Exp $
3    * $Revision: 1.14 $
4    * $Date: 2004/04/18 23:51:37 $
5    *
6    * ====================================================================
7    *
8    *  Copyright 2002-2004 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   */
29  
30  package org.apache.commons.httpclient.methods.multipart;
31  
32  import java.io.ByteArrayOutputStream;
33  import java.io.IOException;
34  import java.io.OutputStream;
35  
36  import org.apache.commons.httpclient.util.EncodingUtil;
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  
40  /***
41   * Abstract class for one Part of a multipart post object.
42   *
43   * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
44   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
45   * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
46   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
47   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
48   *
49   * @since 2.0
50   */
51  public abstract class Part {
52  
53      /*** Log object for this class. */
54      private static final Log LOG = LogFactory.getLog(Part.class);
55  
56      //TODO: Make this configurable
57      
58      /*** The boundary */
59      protected static final String BOUNDARY = "----------------314159265358979323846";
60      
61      /*** The boundary as a byte array */
62      protected static final byte[] BOUNDARY_BYTES = EncodingUtil.getAsciiBytes(BOUNDARY);
63      
64      /*** Carriage return/linefeed */
65      protected static final String CRLF = "\r\n";
66      
67      /*** Carriage return/linefeed as a byte array */
68      protected static final byte[] CRLF_BYTES = EncodingUtil.getAsciiBytes(CRLF);
69      
70      /*** Content dispostion characters */
71      protected static final String QUOTE = "\"";
72      
73      /*** Content dispostion as a byte array */
74      protected static final byte[] QUOTE_BYTES = 
75        EncodingUtil.getAsciiBytes(QUOTE);
76  
77      /*** Extra characters */
78      protected static final String EXTRA = "--";
79      
80      /*** Extra characters as a byte array */
81      protected static final byte[] EXTRA_BYTES = 
82        EncodingUtil.getAsciiBytes(EXTRA);
83      
84      /*** Content dispostion characters */
85      protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name=";
86      
87      /*** Content dispostion as a byte array */
88      protected static final byte[] CONTENT_DISPOSITION_BYTES = 
89        EncodingUtil.getAsciiBytes(CONTENT_DISPOSITION);
90  
91      /*** Content type header */
92      protected static final String CONTENT_TYPE = "Content-Type: ";
93  
94      /*** Content type header as a byte array */
95      protected static final byte[] CONTENT_TYPE_BYTES = 
96        EncodingUtil.getAsciiBytes(CONTENT_TYPE);
97  
98      /*** Content charset */
99      protected static final String CHARSET = "; charset=";
100 
101     /*** Content charset as a byte array */
102     protected static final byte[] CHARSET_BYTES = 
103       EncodingUtil.getAsciiBytes(CHARSET);
104 
105     /*** Content type header */
106     protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: ";
107 
108     /*** Content type header as a byte array */
109     protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = 
110       EncodingUtil.getAsciiBytes(CONTENT_TRANSFER_ENCODING);
111 
112     /***
113      * Return the boundary string.
114      * @return the boundary string
115      */
116     public static String getBoundary() {
117         return BOUNDARY;
118     }
119     
120     /***
121      * Return the name of this part.
122      * @return The name.
123      */
124     public abstract String getName();
125     
126     /***
127      * Returns the content type of this part.
128      * @return the content type, or <code>null</code> to exclude the content type header
129      */
130     public abstract String getContentType();
131 
132     /***
133      * Return the character encoding of this part.
134      * @return the character encoding, or <code>null</code> to exclude the character 
135      * encoding header
136      */
137     public abstract String getCharSet();
138 
139     /***
140      * Return the transfer encoding of this part.
141      * @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header
142      */
143     public abstract String getTransferEncoding();
144 
145     /***
146      * Write the start to the specified output stream
147      * @param out The output stream
148      * @throws IOException If an IO problem occurs.
149      */
150     protected void sendStart(OutputStream out) throws IOException {
151         LOG.trace("enter sendStart(OutputStream out)");
152         out.write(EXTRA_BYTES);
153         out.write(BOUNDARY_BYTES);
154         out.write(CRLF_BYTES);
155     }
156     
157     /***
158      * Write the content disposition header to the specified output stream
159      * 
160      * @param out The output stream
161      * @throws IOException If an IO problem occurs.
162      */
163     protected void sendDispositionHeader(OutputStream out) throws IOException {
164         LOG.trace("enter sendDispositionHeader(OutputStream out)");
165         out.write(CONTENT_DISPOSITION_BYTES);
166         out.write(QUOTE_BYTES);
167         out.write(EncodingUtil.getAsciiBytes(getName()));
168         out.write(QUOTE_BYTES);
169     }
170     
171     /***
172      * Write the content type header to the specified output stream
173      * @param out The output stream
174      * @throws IOException If an IO problem occurs.
175      */
176      protected void sendContentTypeHeader(OutputStream out) throws IOException {
177         LOG.trace("enter sendContentTypeHeader(OutputStream out)");
178         String contentType = getContentType();
179         if (contentType != null) {
180             out.write(CRLF_BYTES);
181             out.write(CONTENT_TYPE_BYTES);
182             out.write(EncodingUtil.getAsciiBytes(contentType));
183             String charSet = getCharSet();
184             if (charSet != null) {
185                 out.write(CHARSET_BYTES);
186                 out.write(EncodingUtil.getAsciiBytes(charSet));
187             }
188         }
189     }
190 
191     /***
192      * Write the content transfer encoding header to the specified 
193      * output stream
194      * 
195      * @param out The output stream
196      * @throws IOException If an IO problem occurs.
197      */
198      protected void sendTransferEncodingHeader(OutputStream out) throws IOException {
199         LOG.trace("enter sendTransferEncodingHeader(OutputStream out)");
200         String transferEncoding = getTransferEncoding();
201         if (transferEncoding != null) {
202             out.write(CRLF_BYTES);
203             out.write(CONTENT_TRANSFER_ENCODING_BYTES);
204             out.write(EncodingUtil.getAsciiBytes(transferEncoding));
205         }
206     }
207 
208     /***
209      * Write the end of the header to the output stream
210      * @param out The output stream
211      * @throws IOException If an IO problem occurs.
212      */
213     protected void sendEndOfHeader(OutputStream out) throws IOException {
214         LOG.trace("enter sendEndOfHeader(OutputStream out)");
215         out.write(CRLF_BYTES);
216         out.write(CRLF_BYTES);
217     }
218     
219     /***
220      * Write the data to the specified output stream
221      * @param out The output stream
222      * @throws IOException If an IO problem occurs.
223      */
224     protected abstract void sendData(OutputStream out) throws IOException;
225     
226     /***
227      * Return the length of the main content
228      * 
229      * @return long The length.
230      * @throws IOException If an IO problem occurs
231      */
232     protected abstract long lengthOfData() throws IOException;
233     
234     /***
235      * Write the end data to the output stream.
236      * @param out The output stream
237      * @throws IOException If an IO problem occurs.
238      */
239     protected void sendEnd(OutputStream out) throws IOException {
240         LOG.trace("enter sendEnd(OutputStream out)");
241         out.write(CRLF_BYTES);
242     }
243     
244     /***
245      * Write all the data to the output stream.
246      * If you override this method make sure to override 
247      * #length() as well
248      * 
249      * @param out The output stream
250      * @throws IOException If an IO problem occurs.
251      */
252     public void send(OutputStream out) throws IOException {
253         LOG.trace("enter send(OutputStream out)");
254         sendStart(out);
255         sendDispositionHeader(out);
256         sendContentTypeHeader(out);
257         sendTransferEncodingHeader(out);
258         sendEndOfHeader(out);
259         sendData(out);
260         sendEnd(out);
261     }
262 
263 
264     /***
265      * Return the full length of all the data.
266      * If you override this method make sure to override 
267      * #send(OutputStream) as well
268      * 
269      * @return long The length.
270      * @throws IOException If an IO problem occurs
271      */
272     public long length() throws IOException {
273         LOG.trace("enter length()");
274         ByteArrayOutputStream overhead = new ByteArrayOutputStream();
275         sendStart(overhead);
276         sendDispositionHeader(overhead);
277         sendContentTypeHeader(overhead);
278         sendTransferEncodingHeader(overhead);
279         sendEndOfHeader(overhead);
280         sendEnd(overhead);
281         return overhead.size() + lengthOfData();
282     }
283 
284     /***
285      * Return a string representation of this object.
286      * @return A string representation of this object.
287      * @see java.lang.Object#toString()
288      */    
289     public String toString() {
290         return this.getName();
291     }
292 
293     /***
294      * Write all parts and the last boundary to the specified output stream
295      * 
296      * @param out The output stream
297      * @param parts The array of parts to be sent
298      * 
299      * @throws IOException If an IO problem occurs.
300      */
301     public static void sendParts(OutputStream out, final Part[] parts)
302     throws IOException {
303         LOG.trace("enter sendParts(OutputStream out, Parts[])");
304         if (parts == null) {
305             throw new IllegalArgumentException("Parts may not be null"); 
306         }
307         for (int i = 0; i < parts.length; i++) {
308             parts[i].send(out);
309         }
310         out.write(EXTRA_BYTES);
311         out.write(BOUNDARY_BYTES);
312         out.write(EXTRA_BYTES);
313         out.write(CRLF_BYTES);
314     }
315 
316     /***
317      * Return the total sum of all parts and that of the last boundary
318      * 
319      * @param parts The array of parts
320      * 
321      * @return the total length
322      * 
323      * @throws IOException If an IO problem occurs.
324      */
325     public static long getLengthOfParts(final Part[] parts)
326     throws IOException {
327         LOG.trace("getLengthOfParts(Parts[])");
328         if (parts == null) {
329             throw new IllegalArgumentException("Parts may not be null"); 
330         }
331         long total = 0;
332         for (int i = 0; i < parts.length; i++) {
333             total += parts[i].length();
334         }
335         total += EXTRA_BYTES.length;
336         total += BOUNDARY_BYTES.length;
337         total += EXTRA_BYTES.length;
338         total += CRLF_BYTES.length;
339         return total;
340     }        
341 }