KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > axis > attachments > MultiPartRelatedInputStream


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.axis.attachments;
17
18 import org.apache.axis.Part;
19 import org.apache.axis.components.logger.LogFactory;
20 import org.apache.axis.transport.http.HTTPConstants;
21 import org.apache.axis.utils.Messages;
22 import org.apache.axis.utils.IOUtils;
23 import org.apache.commons.logging.Log;
24
25 import javax.activation.DataHandler JavaDoc;
26 import javax.mail.internet.MimeUtility JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.BufferedInputStream JavaDoc;
29
30 /**
31  * This simulates the multipart stream.
32  *
33  * @author Rick Rineholt
34  */

35 public class MultiPartRelatedInputStream extends MultiPartInputStream{
36
37     /** Field log */
38     protected static Log log =
39             LogFactory.getLog(MultiPartRelatedInputStream.class.getName());
40
41     /** Field MIME_MULTIPART_RELATED */
42     public static final String JavaDoc MIME_MULTIPART_RELATED = "multipart/related";
43
44     /** Field parts */
45     protected java.util.HashMap JavaDoc parts = new java.util.HashMap JavaDoc();
46
47     /** Field orderedParts */
48     protected java.util.LinkedList JavaDoc orderedParts = new java.util.LinkedList JavaDoc();
49
50     /** Field rootPartLength */
51     protected int rootPartLength = 0;
52
53     /** Field closed */
54     protected boolean closed = false; // If true the stream has been closed.
55

56     /** Field eos */
57     protected boolean eos =
58             false; // This is set once the SOAP packet has reached the end of stream.
59

60     // protected java.io.InputStream is = null; //The orginal multipart/related stream.
61
// This stream controls and manages the boundary.
62

63     /** Field boundaryDelimitedStream */
64     protected org.apache.axis.attachments.BoundaryDelimitedStream boundaryDelimitedStream =
65             null;
66
67     /** Field soapStream */
68     protected java.io.InputStream JavaDoc soapStream =
69             null; // Set the soap stream once found.
70

71     /** Field soapStreamBDS */
72     protected java.io.InputStream JavaDoc soapStreamBDS =
73             null; // Set to the boundary delimited stream assoc. with soap stream once found.
74

75     /** Field boundary */
76     protected byte[] boundary = null;
77
78     /** Field cachedSOAPEnvelope */
79     protected java.io.ByteArrayInputStream JavaDoc cachedSOAPEnvelope =
80             null; // Caches the soap stream if it is
81

82     // Still open and a reference to read data in a later attachment occurs.
83

84     /** Field contentLocation */
85     protected String JavaDoc contentLocation = null;
86
87     /** Field contentId */
88     protected String JavaDoc contentId = null;
89
90     /** Field MAX_CACHED */
91     private static final int MAX_CACHED = 16 * 1024;
92
93     /**
94      * Create a new Multipart stream.
95      * @param contentType the string that holds the contentType
96      * @param stream the true input stream from where the source
97      *
98      * @throws org.apache.axis.AxisFault if the stream could not be created
99      */

100     public MultiPartRelatedInputStream(
101             String JavaDoc contentType, java.io.InputStream JavaDoc stream)
102             throws org.apache.axis.AxisFault {
103
104         super(null); // don't cache this stream.
105

106         if(!(stream instanceof BufferedInputStream JavaDoc)) {
107             stream = new BufferedInputStream JavaDoc(stream);
108         }
109         
110         try {
111
112             // First find the start and boundary parameters. There are real weird rules regard what
113
// can be in real headers what needs to be escaped etc let mail parse it.
114
javax.mail.internet.ContentType JavaDoc ct =
115                     new javax.mail.internet.ContentType JavaDoc(contentType);
116             String JavaDoc rootPartContentId =
117                     ct.getParameter("start"); // Get the root part content.
118

119             if (rootPartContentId != null) {
120                 rootPartContentId = rootPartContentId.trim();
121
122                 if (rootPartContentId.startsWith("<")) {
123                     rootPartContentId = rootPartContentId.substring(1);
124                 }
125
126                 if (rootPartContentId.endsWith(">")) {
127                     rootPartContentId = rootPartContentId.substring(0,
128                             rootPartContentId.length() - 1);
129                 }
130
131             }
132
133             if(ct.getParameter("boundary") != null) {
134                 String JavaDoc boundaryStr =
135                         "--"
136                         + ct.getParameter(
137                                 "boundary"); // The boundary with -- add as always the case.
138

139
140                 // if start is null then the first attachment is the rootpart
141
// First read the start boundary -- this is done with brute force since the servlet may swallow the crlf between headers.
142
// after this we use the more efficient boundarydelimeted stream. There should never be any data here anyway.
143
byte[][] boundaryMarker = new byte[2][boundaryStr.length() + 2];
144
145                 IOUtils.readFully(stream, boundaryMarker[0]);
146
147                 boundary = (boundaryStr + "\r\n").getBytes("US-ASCII");
148
149                 int current = 0;
150
151                 // This just goes brute force one byte at a time to find the first boundary.
152
// in most cases this just a crlf.
153
for (boolean found = false; !found; ++current) {
154                     if (!(found =
155                             java.util.Arrays.equals(boundaryMarker[current & 0x1],
156                                     boundary))) {
157                         System.arraycopy(boundaryMarker[current & 0x1], 1,
158                                 boundaryMarker[(current + 1) & 0x1], 0,
159                                 boundaryMarker[0].length - 1);
160
161                         if (stream.read(
162                                 boundaryMarker[(current + 1) & 0x1],
163                                 boundaryMarker[0].length - 1, 1) < 1) {
164                             throw new org.apache.axis.AxisFault(
165                                     Messages.getMessage(
166                                             "mimeErrorNoBoundary", new String JavaDoc(boundary)));
167                         }
168                     }
169                 }
170
171                 // after the first boundary each boundary will have a cr lf at the beginning since after the data in any part there
172
// is a cr lf added to put the boundary at the begining of a line.
173
boundaryStr = "\r\n" + boundaryStr;
174                 boundary = boundaryStr.getBytes("US-ASCII");
175             } else {
176                 // Since boundary is not specified, we try to find one.
177
for (boolean found = false; !found;) {
178                     boundary= readLine(stream);
179                     if( boundary == null)
180                         throw new org.apache.axis.AxisFault(
181                                 Messages.getMessage(
182                                         "mimeErrorNoBoundary", "--"));
183                      found = boundary.length >4 && boundary[2] == '-' && boundary[3]== '-';
184                 }
185               }
186
187             // create the boundary delmited stream.
188
boundaryDelimitedStream =
189                     new org.apache.axis.attachments.BoundaryDelimitedStream(stream,
190                             boundary, 1024);
191
192             // Now read through all potential streams until we have found the root part.
193
String JavaDoc contentTransferEncoding = null;
194
195             do {
196                 contentId = null;
197                 contentLocation = null;
198                 contentTransferEncoding = null;
199
200                 // Read this attachments headers from the stream.
201
javax.mail.internet.InternetHeaders JavaDoc headers =
202                         new javax.mail.internet.InternetHeaders JavaDoc(
203                                 boundaryDelimitedStream);
204
205                 // Use java mail utility to read through the headers.
206
contentId = headers.getHeader(HTTPConstants.HEADER_CONTENT_ID,
207                         null);
208
209                 // Clean up the headers and remove any < >
210
if (contentId != null) {
211                     contentId = contentId.trim();
212
213                     if (contentId.startsWith("<")) {
214                         contentId = contentId.substring(1);
215                     }
216
217                     if (contentId.endsWith(">")) {
218                         contentId = contentId.substring(0, contentId.length()
219                                 - 1);
220                     }
221
222                     contentId = contentId.trim();
223
224                   // if (!contentId.startsWith("cid:")) {
225
// contentId =
226
// "cid:"
227
// + contentId; // make sure its identified as cid
228
// }
229
}
230
231                 contentLocation =
232                         headers.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION,
233                                 null);
234
235                 if (contentLocation != null) {
236                     contentLocation = contentLocation.trim();
237
238                     if (contentLocation.startsWith("<")) {
239                         contentLocation = contentLocation.substring(1);
240                     }
241
242                     if (contentLocation.endsWith(">")) {
243                         contentLocation = contentLocation.substring(
244                                 0, contentLocation.length() - 1);
245                     }
246
247                     contentLocation = contentLocation.trim();
248                 }
249
250                 contentType =
251                         headers.getHeader(HTTPConstants.HEADER_CONTENT_TYPE, null);
252
253                 if (contentType != null) {
254                     contentType = contentType.trim();
255                 }
256
257                 contentTransferEncoding = headers.getHeader(
258                         HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING, null);
259
260                 if (contentTransferEncoding != null) {
261                     contentTransferEncoding = contentTransferEncoding.trim();
262                 }
263
264                 java.io.InputStream JavaDoc decodedStream = boundaryDelimitedStream;
265
266                 if ((contentTransferEncoding != null)
267                         && (0 != contentTransferEncoding.length())) {
268                     decodedStream = MimeUtility.decode(decodedStream,
269                             contentTransferEncoding);
270                 }
271
272                 if ((rootPartContentId != null) && !rootPartContentId.equals(
273                         contentId)) { // This is a part that has come in prior to the root part. Need to buffer it up.
274
javax.activation.DataHandler JavaDoc dh =
275                             new javax.activation.DataHandler JavaDoc(
276                                     new org.apache.axis.attachments.ManagedMemoryDataSource(
277                                             decodedStream, MAX_CACHED, contentType, true));
278                     AttachmentPart ap = new AttachmentPart(dh);
279
280                     if (contentId != null) {
281                         ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID,
282                                 contentId);
283                     }
284
285                     if (contentLocation != null) {
286                         ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION,
287                                 contentLocation);
288                     }
289
290                     for (java.util.Enumeration JavaDoc en =
291                             headers.getNonMatchingHeaders(new String JavaDoc[]{
292                                 HTTPConstants.HEADER_CONTENT_ID,
293                                 HTTPConstants.HEADER_CONTENT_LOCATION,
294                                 HTTPConstants.HEADER_CONTENT_TYPE}); en.hasMoreElements();) {
295                         javax.mail.Header JavaDoc header =
296                                 (javax.mail.Header JavaDoc) en.nextElement();
297                         String JavaDoc name = header.getName();
298                         String JavaDoc value = header.getValue();
299
300                         if ((name != null) && (value != null)) {
301                             name = name.trim();
302
303                             if (name.length() != 0) {
304                                 ap.addMimeHeader(name, value);
305                             }
306                         }
307                     }
308
309                     addPart(contentId, contentLocation, ap);
310
311                     boundaryDelimitedStream =
312                             boundaryDelimitedStream.getNextStream(); // Gets the next stream.
313
}
314             } while ((null != boundaryDelimitedStream)
315                     && (rootPartContentId != null)
316                     && !rootPartContentId.equals(contentId));
317
318             if (boundaryDelimitedStream == null) {
319                 throw new org.apache.axis.AxisFault(
320                         Messages.getMessage("noRoot", rootPartContentId));
321             }
322
323             soapStreamBDS = boundaryDelimitedStream;
324
325             if ((contentTransferEncoding != null)
326                     && (0 != contentTransferEncoding.length())) {
327                 soapStream = MimeUtility.decode(boundaryDelimitedStream,
328                         contentTransferEncoding);
329             } else {
330                 soapStream =
331                         boundaryDelimitedStream; // This should be the SOAP part
332
}
333
334             // Read from the input stream all attachments prior to the root part.
335
} catch (javax.mail.internet.ParseException JavaDoc e) {
336             throw new org.apache.axis.AxisFault(
337                     Messages.getMessage("mimeErrorParsing", e.getMessage()));
338         } catch (java.io.IOException JavaDoc e) {
339             throw new org.apache.axis.AxisFault(
340                     Messages.getMessage("readError", e.getMessage()));
341         } catch (javax.mail.MessagingException JavaDoc e) {
342             throw new org.apache.axis.AxisFault(
343                     Messages.getMessage("readError", e.getMessage()));
344         }
345     }
346
347     //when searching for a MIME boundary it MUST be terminated with CR LF. LF alone is NOT sufficient.
348
private final byte[] readLine(java.io.InputStream JavaDoc is) throws IOException JavaDoc {
349
350         java.io.ByteArrayOutputStream JavaDoc input = new java.io.ByteArrayOutputStream JavaDoc(1024);
351         int c = 0;
352         input.write('\r');
353         input.write('\n');
354
355         int next = -1;
356         for (;c != -1;) {
357             c = -1 != next ? next : is.read();
358             next = -1;
359             switch (c) {
360                 case -1:
361                 break;
362                 case '\r':
363                     next = is.read();
364                     if(next == '\n') //found a line.
365
return input.toByteArray();
366                     if(next == -1) return null;
367                     //fall through
368
default:
369                     input.write((byte)c);
370                 break;
371             }
372         }
373         //even if there is stuff in buffer if EOF then this can't be a boundary.
374
return null;
375     }
376
377     public Part getAttachmentByReference(final String JavaDoc[] id)
378             throws org.apache.axis.AxisFault {
379
380         // First see if we have read it in yet.
381
Part ret = null;
382
383         for (int i = id.length - 1; (ret == null) && (i > -1); --i) {
384             ret = (AttachmentPart) parts.get(id[i]);
385         }
386
387         if (null == ret) {
388             ret = readTillFound(id);
389         }
390
391         log.debug(Messages.getMessage("return02",
392                 "getAttachmentByReference(\"" + id
393                 + "\"", ((ret == null)
394                 ? "null"
395                 : ret.toString())));
396
397         return ret;
398     }
399
400     /**
401      * Add an <code>AttachmentPart</code> together with its content and location
402      * IDs.
403      *
404      * @param contentId the content ID
405      * @param locationId the location ID
406      * @param ap the <code>AttachmentPart</code>
407      */

408     protected void addPart(String JavaDoc contentId, String JavaDoc locationId,
409                            AttachmentPart ap) {
410
411         if ((contentId != null) && (contentId.trim().length() != 0)) {
412             parts.put(contentId, ap);
413         }
414
415         if ((locationId != null) && (locationId.trim().length() != 0)) {
416             parts.put(locationId, ap);
417         }
418
419         orderedParts.add(ap);
420     }
421
422     /** Field READ_ALL */
423     protected static final String JavaDoc[] READ_ALL = {
424         " * \0 ".intern()}; // Shouldn't never match
425

426     /**
427      * Read all data.
428      *
429      * @throws org.apache.axis.AxisFault if there was a problem reading all the
430      * data
431      */

432     protected void readAll() throws org.apache.axis.AxisFault {
433         readTillFound(READ_ALL);
434     }
435
436     public java.util.Collection JavaDoc getAttachments()
437             throws org.apache.axis.AxisFault {
438
439         readAll();
440
441         return orderedParts;
442     }
443
444     /**
445      * This will read streams in till the one that is needed is found.
446      *
447      * @param id id is the stream being sought.
448      *
449      * @return the part for the id
450      *
451      * @throws org.apache.axis.AxisFault
452      */

453     protected Part readTillFound(final String JavaDoc[] id)
454             throws org.apache.axis.AxisFault {
455
456         if (boundaryDelimitedStream == null) {
457             return null; // The whole stream has been consumed already
458
}
459
460         Part ret = null;
461
462         try {
463             if (soapStreamBDS
464                     == boundaryDelimitedStream) { // Still on the SOAP stream.
465
if (!eos) { // The SOAP packet has not been fully read yet. Need to store it away.
466
java.io.ByteArrayOutputStream JavaDoc soapdata =
467                             new java.io.ByteArrayOutputStream JavaDoc(1024 * 8);
468                     byte[] buf =
469                             new byte[1024 * 16];
470                     int byteread = 0;
471
472                     do {
473                         byteread = soapStream.read(buf);
474
475                         if (byteread > 0) {
476                             soapdata.write(buf, 0, byteread);
477                         }
478                     } while (byteread > -1);
479
480                     soapdata.close();
481
482                     soapStream = new java.io.ByteArrayInputStream JavaDoc(
483                             soapdata.toByteArray());
484                 }
485
486                 boundaryDelimitedStream =
487                         boundaryDelimitedStream.getNextStream();
488             }
489
490             // Now start searching for the data.
491
if (null != boundaryDelimitedStream) {
492                 do {
493                     String JavaDoc contentType = null;
494                     String JavaDoc contentId = null;
495                     String JavaDoc contentTransferEncoding = null;
496                     String JavaDoc contentLocation = null;
497
498                     // Read this attachments headers from the stream.
499
javax.mail.internet.InternetHeaders JavaDoc headers =
500                             new javax.mail.internet.InternetHeaders JavaDoc(
501                                     boundaryDelimitedStream);
502
503                     contentId = headers.getHeader("Content-Id", null);
504
505                     if (contentId != null) {
506                         contentId = contentId.trim();
507
508                         if (contentId.startsWith("<")) {
509                             contentId = contentId.substring(1);
510                         }
511
512                         if (contentId.endsWith(">")) {
513                             contentId =
514                                     contentId.substring(0, contentId.length() - 1);
515                         }
516
517                      // if (!contentId.startsWith("cid:")) {
518
// contentId = "cid:" + contentId;
519
// }
520

521                         contentId = contentId.trim();
522                     }
523
524                     contentType =
525                             headers.getHeader(HTTPConstants.HEADER_CONTENT_TYPE,
526                                     null);
527
528                     if (contentType != null) {
529                         contentType = contentType.trim();
530                     }
531
532                     contentLocation =
533                             headers.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION,
534                                     null);
535
536                     if (contentLocation != null) {
537                         contentLocation = contentLocation.trim();
538                     }
539
540                     contentTransferEncoding = headers.getHeader(
541                             HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING, null);
542
543                     if (contentTransferEncoding != null) {
544                         contentTransferEncoding =
545                                 contentTransferEncoding.trim();
546                     }
547
548                     java.io.InputStream JavaDoc decodedStream = boundaryDelimitedStream;
549
550                     if ((contentTransferEncoding != null)
551                             && (0 != contentTransferEncoding.length())) {
552                         decodedStream =
553                                 MimeUtility.decode(decodedStream,
554                                         contentTransferEncoding);
555                     }
556
557                     ManagedMemoryDataSource source = new ManagedMemoryDataSource(
558                                                         decodedStream, ManagedMemoryDataSource.MAX_MEMORY_DISK_CACHED, contentType, true);
559                     DataHandler JavaDoc dh = new DataHandler JavaDoc(source);
560                     AttachmentPart ap = new AttachmentPart(dh);
561
562                     if (contentId != null) {
563                         ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID,
564                                 contentId);
565                     }
566
567                     if (contentLocation != null) {
568                         ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION,
569                                 contentLocation);
570                     }
571
572                     for (java.util.Enumeration JavaDoc en =
573                             headers.getNonMatchingHeaders(new String JavaDoc[]{
574                                 HTTPConstants.HEADER_CONTENT_ID,
575                                 HTTPConstants.HEADER_CONTENT_LOCATION,
576                                 HTTPConstants.HEADER_CONTENT_TYPE}); en.hasMoreElements();) {
577                         javax.mail.Header JavaDoc header =
578                                 (javax.mail.Header JavaDoc) en.nextElement();
579                         String JavaDoc name = header.getName();
580                         String JavaDoc value = header.getValue();
581
582                         if ((name != null) && (value != null)) {
583                             name = name.trim();
584
585                             if (name.length() != 0) {
586                                 ap.addMimeHeader(name, value);
587                             }
588                         }
589                     }
590
591                     addPart(contentId, contentLocation, ap);
592
593                     for (int i = id.length - 1; (ret == null) && (i > -1);
594                          --i) {
595                         if ((contentId != null) && id[i].equals(
596                                 contentId)) { // This is the part being sought
597
ret = ap;
598                         } else if ((contentLocation != null)
599                                 && id[i].equals(contentLocation)) {
600                             ret = ap;
601                         }
602                     }
603
604                     boundaryDelimitedStream =
605                             boundaryDelimitedStream.getNextStream();
606                 } while ((null == ret) && (null != boundaryDelimitedStream));
607             }
608         } catch (Exception JavaDoc e) {
609             throw org.apache.axis.AxisFault.makeFault(e);
610         }
611
612         return ret;
613     }
614
615     public String JavaDoc getContentLocation() {
616         return contentLocation;
617     }
618
619     public String JavaDoc getContentId() {
620         return contentId;
621     }
622
623     public int read(byte[] b, int off, int len) throws java.io.IOException JavaDoc {
624
625         if (closed) {
626             throw new java.io.IOException JavaDoc(Messages.getMessage("streamClosed"));
627         }
628
629         if (eos) {
630             return -1;
631         }
632
633         int read = soapStream.read(b, off, len);
634
635         if (read < 0) {
636             eos = true;
637         }
638
639         return read;
640     }
641
642     public int read(byte[] b) throws java.io.IOException JavaDoc {
643         return read(b, 0, b.length);
644     }
645
646     public int read() throws java.io.IOException JavaDoc {
647
648         if (closed) {
649             throw new java.io.IOException JavaDoc(Messages.getMessage("streamClosed"));
650         }
651
652         if (eos) {
653             return -1;
654         }
655
656         int ret = soapStream.read();
657
658         if (ret < 0) {
659             eos = true;
660         }
661
662         return ret;
663     }
664
665     public void close() throws java.io.IOException JavaDoc {
666
667         closed = true;
668
669         soapStream.close();
670     }
671
672     public int available() throws java.io.IOException JavaDoc {
673         return (closed || eos) ? 0 : soapStream.available();
674     }
675
676 }
677
Popular Tags