KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > soap > rpc > SOAPContext


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 2000 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "SOAP" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 2000, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package org.apache.soap.rpc;
59
60 import java.io.*;
61 import java.util.*;
62 import org.apache.soap.util.*;
63 import org.apache.soap.*;
64 import org.apache.soap.encoding.*;
65 import org.apache.soap.server.*;
66 import org.apache.soap.util.mime.*;
67 import javax.mail.*;
68 import javax.mail.internet.*;
69 import javax.activation.*;
70
71 /**
72  * SOAP context for a message. Encapsulates:
73  * <ul>
74  * <li>MIME multipart
75  * </ul>
76  *
77  * @author Wouter Cloetens (wcloeten@raleigh.ibm.com)
78  */

79 public class SOAPContext {
80     protected MimeMultipart parts;
81     protected Hashtable bag = new Hashtable();
82     protected ClassLoader JavaDoc loader = null ;
83
84     /**
85      * This flag indicates if setRootPart() was called, so we can distinguish
86      * default root part resolution from deliberate root part indication.
87      * Effect: calling setRootPart() twice will <strong>replace</strong> the
88      * original root part.
89      */

90     protected boolean rootPartSet = false;
91
92     /**
93      * List of MIME headers to strip from JavaMail-generated header.
94      */

95     private static final String JavaDoc[] ignoreHeaders =
96         {"Message-ID", "Mime-Version"};
97
98     private static final String JavaDoc DEFAULT_BASEURI = "thismessage:/";
99
100     /**
101      * Constructor.
102      */

103     public SOAPContext() {
104         parts = null;
105     }
106
107     /**
108      * Initialise MIME multipart object from a data source.
109      *
110      * @param ds a DataSource object to read from
111      */

112     public void readMultipart(DataSource ds) throws MessagingException {
113         parts = new MimeMultipart(ds);
114     }
115
116     /**
117      * Get the specified Part. Parts are numbered starting at 0.
118      *
119      * @param index the index of the desired Part
120      * @return the Part, or null if no such part exists.
121      */

122     public MimeBodyPart getBodyPart(int index) {
123       /* Actually, this method never throws a MessagingException. In case a
124        * future implementation does, catch it and throw an
125        * IndexOutOfBoundsException
126        */

127         if (parts == null) {
128             return null;
129         }
130         try {
131             return (MimeBodyPart)parts.getBodyPart(index);
132         }
133         catch (MessagingException me) {
134             throw new IndexOutOfBoundsException JavaDoc(me.getMessage());
135         }
136         catch (IndexOutOfBoundsException JavaDoc iobe) {
137             return null;
138         }
139         catch (NullPointerException JavaDoc npe) {
140             return null;
141         }
142     }
143
144     /**
145      * Get the Mimepart referred to by the given ContentID (CID).
146      * Returns null if the part is not found.
147      *
148      * @param CID the ContentID of the desired part
149      * @return the Part, or null if no such part exists.
150      */

151     public MimeBodyPart getBodyPart(String JavaDoc CID) {
152         if (parts == null) {
153             return null;
154         }
155         try {
156             return (MimeBodyPart)parts.getBodyPart(CID);
157         }
158         catch (MessagingException me) {
159             return null;
160         }
161         catch (NullPointerException JavaDoc npe) {
162             return null;
163         }
164         catch (IndexOutOfBoundsException JavaDoc iobe) {
165             return null;
166         }
167     }
168
169     /**
170      * Find the Mimepart referred to by the given URI. This can be:<ul>
171      * <li>An absolute URI, in which case a part is located with a
172      * corresponding Content-Location header.
173      * <li>A relative URI, in which case an absolute URI is constructed
174      * relative to the Content-Location header of the root SOAP part
175      * or the multipart and the previous rule is then applied.
176      * <li>A URI of the format "cid:xxx"> in which case a part is located
177      * with a matching Content-ID header.
178      * </ul>Returns null if the part is not found.
179      * <p>Note: relative URIs not entirely implemented yet.
180      *
181      * @param uri the URI
182      * @return the Part or null if not found
183      */

184     public MimeBodyPart findBodyPart(String JavaDoc uri) {
185         if (parts == null || uri == null) {
186             return null;
187         }
188         try {
189             if (uri.length() > 4 &&
190                uri.substring(0, 4).equalsIgnoreCase("cid:")) {
191                 // It's a Content-ID. URLDecode and lookup.
192
String JavaDoc cid = MimeUtils.decode(uri.substring(4));
193                 // References are not supposed to be inside brackets, but be
194
// tolerant.
195
if (cid.charAt(0) != '<' || cid.charAt(cid.length()) != '>')
196                     cid = '<' + cid + '>';
197
198                 try {
199                     return (MimeBodyPart)parts.getBodyPart(cid);
200                 } catch(NullPointerException JavaDoc npe) {
201                 }
202             } else {
203                 // It's a URI.
204
return findPartByLocation(uri);
205             }
206
207         } catch (MessagingException me) {
208         } catch (NullPointerException JavaDoc npe) {
209         }
210         return null;
211     }
212
213     /**
214      * Determine the document's base URI, according to the following scheme:
215      * <ol>
216      * <li>The Content-Location header of the root (SOAP) part.
217      * <li>The Content-Location header of the multipart. (not implemented)
218      * <li>"thismessage:/"
219      * </ol>
220      */

221     public String JavaDoc getBaseURI() {
222         String JavaDoc baseUri = null;
223         try {
224             baseUri = getRootPart().getHeader(
225                 Constants.HEADER_CONTENT_LOCATION, null);
226         } catch(MessagingException me) {
227         }
228         if (baseUri == null)
229             baseUri = DEFAULT_BASEURI;
230         return baseUri;
231     }
232
233     /**
234      * Find the Mimepart referred to by the given URI. This can be:<ul>
235      * <li>An absolute URI, in which case a part is located with a
236      * corresponding Content-Location header.
237      * <li>A relative URI, in which case an absolute URI is constructed
238      * relative to the Content-Location header of the root SOAP part
239      * or the multipart and the previous rule is then applied.
240      * </ul>Returns null if the part is not found.
241      * <p>Note: relative URIs not entirely implemented yet. We also can't pick
242      * up the base URI from the multipart (transport) headers yet, only from
243      * the root (SOAP) part.
244      *
245      * @param uri the URI
246      * @return the Part or null if not found
247      */

248     public MimeBodyPart findPartByLocation(String JavaDoc uri) {
249         try {
250             String JavaDoc baseUri = getBaseURI();
251             uri = normalizeURI(uri, baseUri);
252             if (uri == null)
253                 return null;
254             for (int i = 0; i < parts.getCount(); i++) {
255                 MimeBodyPart part = getBodyPart(i);
256                 if (part != null) {
257                     String JavaDoc partUri = part.getHeader(
258                         Constants.HEADER_CONTENT_LOCATION, null);
259                     if (uri.equals(normalizeURI(partUri, baseUri)))
260                         return part;
261                 }
262             }
263         } catch(MessagingException me) {
264         }
265         return null;
266     }
267
268     /**
269      * Normalise URI to an absolute URI according to rules in RFC2396.
270      * <p>Note: resolution for relative URIs is not completely implemented
271      * yet. For now, we just concatenate the relative URI to the base URI.
272      */

273     private String JavaDoc normalizeURI(String JavaDoc uri, String JavaDoc baseUri) {
274         // Check if it is an absolute URI.
275
int p1 = uri.indexOf(':');
276         if (p1 >= 0) {
277             String JavaDoc scheme = uri.substring(0, p1);
278             if (scheme.indexOf('/') == -1 &&
279                 scheme.indexOf('?') == -1 &&
280                 scheme.indexOf('#') == -1)
281                 // Yes, it's absolute! Return unchanged.
282
return uri;
283         }
284         // It's a relative URI. Normalise to an absolute URI.
285
return baseUri + uri;
286     }
287
288     /**
289      * Adds a Part. The BodyPart is appended to
290      * the list of existing Parts.
291      *
292      * @param part The Part to be appended
293      * @exception MessagingException
294      * @exception IllegalWriteException if the underlying
295      * implementation does not support modification
296      * of existing values
297      */

298     public void addBodyPart(MimeBodyPart part) throws MessagingException {
299         // Implemented as addBodyPart(part, index=-1) below.
300
addBodyPart(part, -1);
301     }
302
303     /**
304      * Adds a MimeBodyPart at position <code>index</code>.
305      * If <code>index</code> is not the last one in the list,
306      * the subsequent parts are shifted up. If <code>index</code>
307      * is larger than the number of parts present, the
308      * MimeBodyPart is appended to the end.
309      *
310      * @param part The BodyPart to be inserted
311      * @param index Location where to insert the part
312      * @exception MessagingException
313      * @exception IllegalWriteException if the underlying
314      * implementation does not support modification
315      * of existing values
316      * @exception IllegalArgumentException if the part cannot be made
317      * into an attachment for some reason
318      */

319     public void addBodyPart(MimeBodyPart part, int index)
320         throws MessagingException, IllegalArgumentException JavaDoc {
321         if (parts == null)
322             parts = new MimeMultipart(
323                 Constants.HEADERVAL_MULTIPART_CONTENT_SUBTYPE);
324         // set some Mime headers. Assume that a passed MimeBodyPart
325
// already has these set to appropriate values
326
DataHandler dh = part.getDataHandler();
327         try {
328             MimeType ctype = new MimeType(dh.getContentType());
329             part.setHeader(Constants.HEADER_CONTENT_TYPE,
330                            ctype.toString());
331             if (dh.getDataSource() instanceof ByteArrayDataSource)
332                 part.setHeader(Constants.HEADER_CONTENT_LENGTH,
333                              String.valueOf(((ByteArrayDataSource)dh
334                                              .getDataSource())
335                                             .getSize()));
336             /* NOTE. The following part will do some guesswork to determine if
337              * 8-bit encoding should be used. Even then, JavaMail may still
338              * use base64 encoding for binary types that aren't in the list
339              * below, or quoted-printable for text. Not all SOAP MIME
340              * implemenations may support base64 or quoted-printable, so this
341              * should be replaced with code that always uses 8-bit encoding
342              * exception for text/*, which should be UTF-8 encoded.
343              * To do: find a way to do the latter...
344              */

345             if (ctype.match("application/octet-stream") ||
346                 ctype.match("image/*") ||
347                 ctype.match("audio/*") ||
348                 ctype.match("video/*"))
349                 part.setHeader("Content-Transfer-Encoding", "8bit");
350         } catch(MessagingException me) {
351             throw new IllegalArgumentException JavaDoc(
352                 "Invalid InputStream/DataSource/DataHandler metadata: "
353                 + me);
354         } catch(MimeTypeParseException mtpe) {
355             throw new IllegalArgumentException JavaDoc("Invalid Mime type \""
356                                                + dh.getContentType()
357                                                + "\": " + mtpe);
358         }
359         if (index == -1)
360             parts.addBodyPart(part);
361         else
362             parts.addBodyPart(part, index);
363     }
364
365     /**
366      * Remove a body part.
367      */

368     public void removeBodyPart(MimeBodyPart part) throws MessagingException {
369       parts.removeBodyPart(part);
370     }
371
372     /**
373      * Adds the root BodyPart. Add it in the first position, create a
374      * unique Content ID and set the "start" Content-Type header modifier
375      * that that.<p>
376      * Calling this method twice will <strong>replace</strong> the previous
377      * root part with the new one.
378      *
379      * @param part The BodyPart to be inserted
380      * @exception MessagingException
381      */

382     public void setRootPart(MimeBodyPart part) throws MessagingException {
383         String JavaDoc rootCid = '<' + MimeUtils.getUniqueValue() + '>';
384         part.setHeader(Constants.HEADER_CONTENT_ID, rootCid);
385         if (rootPartSet)
386             parts.removeBodyPart(getRootPart());
387         addBodyPart(part, 0);
388         rootPartSet = true;
389     }
390
391     /**
392      * Set the root part to a provided string (usually the SOAP envelope).
393      * The string will be UTF-8 encoded.
394      *
395      * @param s The String to be inserted
396      * @param contentType the Content-Type of the root part
397      * @exception MessagingException
398      */

399     public void setRootPart(String JavaDoc s, String JavaDoc contentType)
400         throws MessagingException, IOException {
401         setRootPart(s.getBytes("UTF8"), contentType);
402     }
403
404     /**
405      * Set the root part to a provided string (usually the SOAP envelope).
406      * The string will be UTF-8 encoded.
407      *
408      * @param b The String to be inserted
409      * @param contentType the Content-Type of the root part
410      * @return "start" Content-Type header modifier
411      * @exception MessagingException
412      */

413     public void setRootPart(byte[] b, String JavaDoc contentType)
414         throws MessagingException {
415         ByteArrayDataSource ds = new ByteArrayDataSource(b, contentType);
416         DataHandler dh = new DataHandler(ds);
417         MimeBodyPart bp = new MimeBodyPart();
418         bp.setDataHandler(dh);
419         bp.setHeader(Constants.HEADER_CONTENT_LENGTH,
420                      String.valueOf(ds.getSize()));
421
422          // Avoid letting JavaMail determine a transfer-encoding of
423
// quoted-printable or base64... Force 8-bit encoding.
424
bp.setHeader("Content-Transfer-Encoding", "8bit");
425
426         setRootPart(bp);
427     }
428
429     /**
430      * Find the root part. For multipart, search for a "start" Content-Type
431      * header modifier, if not present or invalid, assume the first part is
432      * the root.
433      *
434      * @return document root BodyPart
435      * @exception MessagingException
436      */

437     public MimeBodyPart getRootPart() throws MessagingException {
438         MimeBodyPart rootPart = null;
439         if (getCount() > 1) {
440             String JavaDoc startCid = new ContentType(
441                 parts.getContentType()).getParameter("start");
442             if (startCid != null)
443                 rootPart = getBodyPart(MimeUtils.decode(startCid));
444         }
445         if (rootPart == null)
446             rootPart = getBodyPart(0);
447         return rootPart;
448     }
449
450     /**
451      * Set the MultiPart Mime subtype. This method should be invoked only on
452      * a new MimeMultipart object created by the client. The default subtype
453      * of such a multipart object is "related".<p>
454      *
455      * @param subtype Subtype
456      * @exception MessagingException
457      */

458     public void setSubType(String JavaDoc subtype) throws MessagingException {
459         if (parts == null)
460             parts = new MimeMultipart(subtype);
461         else
462             parts.setSubType(subtype);
463     }
464
465     /**
466      * Returns true is setRootPart() has been called.
467      */

468     public boolean isRootPartSet() {
469         return rootPartSet;
470     }
471
472     /**
473      * Return the number of enclosed BodyPart objects.
474      *
475      * @return number of parts
476      */

477     public int getCount() throws MessagingException {
478         if (parts == null)
479             return 0;
480         else
481             return parts.getCount();
482     }
483
484     /**
485      * Return the content-type
486      *
487      * @return content type of the Mime multipart if there's more
488      * than one part, or of the root part if there's only
489      * one part
490      */

491     public String JavaDoc getContentType() throws MessagingException {
492         if (parts == null)
493             return null;
494         else
495             if (parts.getCount() == 1)
496                 return getRootPart().getContentType();
497             else
498                 return parts.getContentType();
499     }
500
501     /**
502      * Encode the root part or multipart and write to an OutputStream.
503      *
504      * @param os stream to write to
505      * @exception IOException
506      * @exception MessagingException
507      */

508     public void writeTo(OutputStream os)
509         throws IOException, MessagingException {
510         int count = getCount();
511         if (count == 0)
512             throw new IOException("Message is empty!");
513         else if (count == 1) {
514             getRootPart().writeTo(os);
515         }
516         else {
517             Session session = Session.getDefaultInstance(new Properties(), null);
518             MimeMessage msg = new MimeMessage(session);
519             msg.setContent(parts);
520             msg.saveChanges();
521             msg.writeTo(os, ignoreHeaders);
522         }
523     }
524
525     /**
526      * Store something in the hold-all 'bag'
527      */

528     public void setProperty(String JavaDoc name, Object JavaDoc value) {
529       bag.put( name, value );
530     }
531
532     /**
533      * Look for something in the hold-all 'bag'
534      */

535     public Object JavaDoc getProperty(String JavaDoc name) {
536       return( bag.get(name) );
537     }
538
539     /**
540      * Remove something from the hold-all 'bag'
541      */

542     public Object JavaDoc removeProperty(String JavaDoc name) {
543       return( bag.remove(name) );
544     }
545
546     /**
547      * Return the entire list of 'names' in the hold-all 'bag'
548      */

549     public Enumeration getPropertyNames() {
550       return( bag.keys() );
551     }
552
553     public void setClassLoader(ClassLoader JavaDoc cl) {
554       loader = cl;
555     }
556
557     public ClassLoader JavaDoc getClassLoader() {
558       return loader ;
559     }
560
561     public Class JavaDoc loadClass(String JavaDoc className) throws ClassNotFoundException JavaDoc {
562       if ( loader == null )
563         return( Class.forName( className ) );
564       return( Class.forName( className, true, loader ) );
565     }
566
567     /**
568      * String representation for debug purposes.
569      */

570     public String JavaDoc toString() {
571         StringWriter sw = new StringWriter();
572         PrintWriter pw = new PrintWriter(sw);
573
574         pw.print("[Parts={");
575
576         if (parts != null) {
577             try {
578                 for (int i = 0; i < getCount(); i++) {
579                     if (i > 0) {
580                         pw.print(", ");
581                     }
582
583                     MimeBodyPart mbp = getBodyPart(i);
584                     pw.print("[cid:" + mbp.getContentID()
585                              + " type: " + mbp.getContentType()
586                              + " enc: " + mbp.getEncoding() + "]");
587                 }
588             }
589             catch(MessagingException me) {
590                 me.printStackTrace();
591             }
592         }
593
594         pw.print("}]");
595
596         return sw.toString();
597     }
598 }
599
Popular Tags