View Javadoc

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