KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright (C) The Apache Software Foundation. All rights reserved.
3  *
4  * This software is published under the terms of the Apache Software License
5  * version 1.1, a copy of which has been included with this distribution in
6  * the docs/licenses/apache-1.1.txt file.
7  */

8 package org.jboss.axis.attachments;
9
10 import org.jboss.axis.AxisFault;
11 import org.jboss.axis.Part;
12 import org.jboss.axis.transport.http.HTTPConstants;
13 import org.jboss.axis.utils.Messages;
14 import org.jboss.logging.Logger;
15
16 import javax.activation.DataHandler JavaDoc;
17 import javax.mail.Header JavaDoc;
18 import javax.mail.MessagingException JavaDoc;
19 import javax.mail.internet.ContentType JavaDoc;
20 import javax.mail.internet.InternetHeaders JavaDoc;
21 import javax.mail.internet.MimeUtility JavaDoc;
22 import javax.mail.internet.ParseException JavaDoc;
23 import java.io.ByteArrayInputStream JavaDoc;
24 import java.io.ByteArrayOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.util.Arrays JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Enumeration JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.LinkedList JavaDoc;
32
33 /**
34  * This simulates the multipart stream
35  *
36  * @author Rick Rineholt
37  */

38 public class MultiPartRelatedInputStream extends MultiPartInputStream
39 {
40
41    /**
42     * Field log
43     */

44    private static Logger log = Logger.getLogger(MultiPartRelatedInputStream.class.getName());
45
46    /**
47     * Field MIME_MULTIPART_RELATED
48     */

49    public static final String JavaDoc MIME_MULTIPART_RELATED = "multipart/related";
50
51    /**
52     * Field parts
53     */

54    protected HashMap JavaDoc parts = new HashMap JavaDoc();
55
56    /**
57     * Field orderedParts
58     */

59    protected LinkedList JavaDoc orderedParts = new LinkedList JavaDoc();
60
61    /**
62     * Field rootPartLength
63     */

64    protected int rootPartLength = 0;
65
66    /**
67     * Field closed
68     */

69    protected boolean closed = false; // If true the stream has been closed.
70

71    /**
72     * Field eos
73     */

74    protected boolean eos =
75            false; // This is set once the SOAP packet has reached the end of stream.
76

77    // protected InputStream is = null; //The orginal multipart/related stream.
78
// This stream controls and manages the boundary.
79

80    /**
81     * Field boundaryDelimitedStream
82     */

83    protected BoundaryDelimitedStream boundaryDelimitedStream =
84            null;
85
86    /**
87     * Field soapStream
88     */

89    protected InputStream JavaDoc soapStream =
90            null; // Set the soap stream once found.
91

92    /**
93     * Field soapStreamBDS
94     */

95    protected InputStream JavaDoc soapStreamBDS =
96            null; // Set to the boundary delimited stream assoc. with soap stream once found.
97

98    /**
99     * Field boundary
100     */

101    protected byte[] boundary = null;
102
103    /**
104     * Field cachedSOAPEnvelope
105     */

106    protected ByteArrayInputStream JavaDoc cachedSOAPEnvelope =
107            null; // Caches the soap stream if it is
108

109    // Still open and a reference to read data in a later attachment occurs.
110

111    /**
112     * Field contentLocation
113     */

114    protected String JavaDoc contentLocation = null;
115
116    /**
117     * Field contentId
118     */

119    protected String JavaDoc contentId = null;
120
121    /**
122     * Multipart stream.
123     *
124     * @param contentType the string that holds the contentType
125     * @param stream is the true input stream from where the source.
126     * @throws org.jboss.axis.AxisFault
127     */

128    public MultiPartRelatedInputStream(String JavaDoc contentType, InputStream JavaDoc stream)
129            throws AxisFault
130    {
131
132       super(null); // don't cache this stream.
133

134       try
135       {
136
137          // First find the start and boundary parameters. There are real weird rules regard what
138
// can be in real headers what needs to be escaped etc let mail parse it.
139
ContentType JavaDoc ct =
140                  new ContentType JavaDoc(contentType);
141          String JavaDoc rootPartContentId =
142                  ct.getParameter("start"); // Get the root part content.
143

144          if (rootPartContentId != null)
145          {
146             rootPartContentId = rootPartContentId.trim();
147
148             if (rootPartContentId.startsWith("<"))
149             {
150                rootPartContentId = rootPartContentId.substring(1);
151             }
152
153             if (rootPartContentId.endsWith(">"))
154             {
155                rootPartContentId = rootPartContentId.substring(0,
156                        rootPartContentId.length() - 1);
157             }
158
159          }
160
161          if (ct.getParameter("boundary") != null)
162          {
163             String JavaDoc boundaryStr =
164                     "--"
165                     + ct.getParameter("boundary"); // The boundary with -- add as always the case.
166

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

432    public Part getAttachmentByReference(final String JavaDoc[] id)
433            throws AxisFault
434    {
435
436       // First see if we have read it in yet.
437
Part ret = null;
438
439       for (int i = id.length - 1; (ret == null) && (i > -1); --i)
440       {
441          ret = (AttachmentPartImpl)parts.get(id[i]);
442       }
443
444       if (null == ret)
445       {
446          ret = readTillFound(id);
447       }
448
449       log.debug(Messages.getMessage("return02",
450               "getAttachmentByReference(\"" + id
451               + "\"", ((ret == null)
452               ? "null"
453               : ret.toString())));
454
455       return ret;
456    }
457
458    /**
459     * Method addPart
460     *
461     * @param contentId
462     * @param locationId
463     * @param ap
464     */

465    protected void addPart(String JavaDoc contentId, String JavaDoc locationId,
466                           AttachmentPartImpl ap)
467    {
468
469       if ((contentId != null) && (contentId.trim().length() != 0))
470       {
471          parts.put(contentId, ap);
472       }
473
474       if ((locationId != null) && (locationId.trim().length() != 0))
475       {
476          parts.put(locationId, ap);
477       }
478
479       orderedParts.add(ap);
480    }
481
482    /**
483     * Field READ_ALL
484     */

485    protected static final String JavaDoc[] READ_ALL = {
486       " * \0 ".intern()}; // Shouldn't never match
487

488    /**
489     * Method readAll
490     *
491     * @throws AxisFault
492     */

493    protected void readAll() throws AxisFault
494    {
495       readTillFound(READ_ALL);
496    }
497
498    /**
499     * Method getAttachments
500     *
501     * @return the collection of attachments
502     * @throws AxisFault
503     */

504    public Collection JavaDoc getAttachments()
505            throws AxisFault
506    {
507
508       readAll();
509
510       return orderedParts;
511    }
512
513    /**
514     * This will read streams in till the one that is needed is found.
515     *
516     * @param id id is the stream being sought.
517     * @return the part for the id
518     * @throws AxisFault
519     */

520    protected Part readTillFound(final String JavaDoc[] id)
521            throws AxisFault
522    {
523
524       if (boundaryDelimitedStream == null)
525       {
526          return null; // The whole stream has been consumed already
527
}
528
529       Part ret = null;
530
531       try
532       {
533          if (soapStreamBDS
534                  == boundaryDelimitedStream)
535          { // Still on the SOAP stream.
536
if (!eos)
537             { // The SOAP packet has not been fully read yet. Need to store it away.
538
ByteArrayOutputStream JavaDoc soapdata =
539                        new ByteArrayOutputStream JavaDoc(1024 * 8);
540                byte[] buf =
541                        new byte[1024 * 16];
542                int byteread = 0;
543
544                do
545                {
546                   byteread = soapStream.read(buf);
547
548                   if (byteread > 0)
549                   {
550                      soapdata.write(buf, 0, byteread);
551                   }
552                }
553                while (byteread > -1);
554
555                soapdata.close();
556
557                soapStream = new ByteArrayInputStream JavaDoc(soapdata.toByteArray());
558             }
559
560             boundaryDelimitedStream =
561                     boundaryDelimitedStream.getNextStream();
562          }
563
564          // Now start searching for the data.
565
if (null != boundaryDelimitedStream)
566          {
567             do
568             {
569                String JavaDoc contentType = null;
570                String JavaDoc contentId = null;
571                String JavaDoc contentTransferEncoding = null;
572                String JavaDoc contentLocation = null;
573
574                // Read this attachments headers from the stream.
575
InternetHeaders JavaDoc headers =
576                        new InternetHeaders JavaDoc(boundaryDelimitedStream);
577
578                contentId = headers.getHeader("Content-Id", null);
579
580                if (contentId != null)
581                {
582                   contentId = contentId.trim();
583
584                   if (contentId.startsWith("<"))
585                   {
586                      contentId = contentId.substring(1);
587                   }
588
589                   if (contentId.endsWith(">"))
590                   {
591                      contentId =
592                              contentId.substring(0, contentId.length() - 1);
593                   }
594
595                   // if (!contentId.startsWith("cid:")) {
596
// contentId = "cid:" + contentId;
597
// }
598

599                   contentId = contentId.trim();
600                }
601
602                contentType =
603                        headers.getHeader(HTTPConstants.HEADER_CONTENT_TYPE,
604                                null);
605
606                if (contentType != null)
607                {
608                   contentType = contentType.trim();
609                }
610
611                contentLocation =
612                        headers.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION,
613                                null);
614
615                if (contentLocation != null)
616                {
617                   contentLocation = contentLocation.trim();
618                }
619
620                contentTransferEncoding = headers.getHeader(HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING, null);
621
622                if (contentTransferEncoding != null)
623                {
624                   contentTransferEncoding =
625                           contentTransferEncoding.trim();
626                }
627
628                InputStream JavaDoc decodedStream = boundaryDelimitedStream;
629
630                if ((contentTransferEncoding != null)
631                        && (0 != contentTransferEncoding.length()))
632                {
633                   decodedStream =
634                           MimeUtility.decode(decodedStream,
635                                   contentTransferEncoding);
636                }
637
638                ManagedMemoryDataSource source = new ManagedMemoryDataSource(decodedStream, contentType);
639                DataHandler JavaDoc dh = new DataHandler JavaDoc(source);
640                AttachmentPartImpl ap = new AttachmentPartImpl(dh);
641
642                if (contentId != null)
643                {
644                   ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID,
645                           contentId);
646                }
647
648                if (contentLocation != null)
649                {
650                   ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION,
651                           contentLocation);
652                }
653
654                for (Enumeration JavaDoc en =
655                        headers.getNonMatchingHeaders(new String JavaDoc[]{
656                           HTTPConstants.HEADER_CONTENT_ID,
657                           HTTPConstants.HEADER_CONTENT_LOCATION,
658                           HTTPConstants.HEADER_CONTENT_TYPE}); en.hasMoreElements();)
659                {
660                   Header JavaDoc header =
661                           (Header JavaDoc)en.nextElement();
662                   String JavaDoc name = header.getName();
663                   String JavaDoc value = header.getValue();
664
665                   if ((name != null) && (value != null))
666                   {
667                      name = name.trim();
668
669                      if (name.length() != 0)
670                      {
671                         ap.addMimeHeader(name, value);
672                      }
673                   }
674                }
675
676                addPart(contentId, contentLocation, ap);
677
678                for (int i = id.length - 1; (ret == null) && (i > -1);
679                     --i)
680                {
681                   if ((contentId != null) && id[i].equals(contentId))
682                   { // This is the part being sought
683
ret = ap;
684                   }
685                   else if ((contentLocation != null)
686                           && id[i].equals(contentLocation))
687                   {
688                      ret = ap;
689                   }
690                }
691
692                boundaryDelimitedStream =
693                        boundaryDelimitedStream.getNextStream();
694             }
695             while ((null == ret) && (null != boundaryDelimitedStream));
696          }
697       }
698       catch (Exception JavaDoc e)
699       {
700          throw AxisFault.makeFault(e);
701       }
702
703       return ret;
704    }
705
706    /**
707     * Return the content location.
708     *
709     * @return the Content-Location of the stream.
710     * Null if no content-location specified.
711     */

712    public String JavaDoc getContentLocation()
713    {
714       return contentLocation;
715    }
716
717    /**
718     * Return the content id of the stream
719     *
720     * @return the Content-Location of the stream.
721     * Null if no content-location specified.
722     */

723    public String JavaDoc getContentId()
724    {
725       return contentId;
726    }
727
728    /**
729     * Read the root stream.
730     *
731     * @param b
732     * @param off
733     * @param len
734     * @return
735     * @throws IOException
736     */

737    public int read(byte[] b, int off, int len) throws IOException JavaDoc
738    {
739
740       if (closed)
741       {
742          throw new IOException JavaDoc(Messages.getMessage("streamClosed"));
743       }
744
745       if (eos)
746       {
747          return -1;
748       }
749
750       int read = soapStream.read(b, off, len);
751
752       if (read < 0)
753       {
754          eos = true;
755       }
756
757       return read;
758    }
759
760    /**
761     * Method read
762     *
763     * @param b
764     * @return
765     * @throws IOException
766     */

767    public int read(byte[] b) throws IOException JavaDoc
768    {
769       return read(b, 0, b.length);
770    }
771
772    /**
773     * Method read
774     *
775     * @return
776     * @throws IOException
777     */

778    public int read() throws IOException JavaDoc
779    {
780
781       if (closed)
782       {
783          throw new IOException JavaDoc(Messages.getMessage("streamClosed"));
784       }
785
786       if (eos)
787       {
788          return -1;
789       }
790
791       int ret = soapStream.read();
792
793       if (ret < 0)
794       {
795          eos = true;
796       }
797
798       return ret;
799    }
800
801    /**
802     * Method close
803     *
804     * @throws IOException
805     */

806    public void close() throws IOException JavaDoc
807    {
808
809       closed = true;
810
811       soapStream.close();
812    }
813 }
814
Popular Tags