KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > soap > transport > TransportMessage


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.transport;
59
60 import java.io.*;
61 import java.util.*;
62 import javax.activation.*;
63 import javax.mail.*;
64 import javax.mail.internet.*;
65 import javax.xml.parsers.*;
66 import org.w3c.dom.*;
67 import org.xml.sax.*;
68 import org.apache.soap.*;
69 import org.apache.soap.encoding.*;
70 import org.apache.soap.rpc.*;
71 import org.apache.soap.util.*;
72 import org.apache.soap.util.xml.*;
73 import org.apache.soap.util.mime.*;
74 import org.apache.soap.transport.EnvelopeEditor;
75
76 /**
77  * Transport type-independent encapsulation of SOAP message content.
78  *
79  * @author Wouter Cloetens
80  */

81 public class TransportMessage implements Serializable {
82     protected String JavaDoc contentType = null;
83     protected int offset = 0;
84     protected byte[] bytes = null;
85     protected String JavaDoc envelope = null;
86     protected Hashtable headers = null;
87     protected SOAPContext ctx = null;
88
89     /**
90      * No-argument constructor.
91      */

92     public TransportMessage() {
93     }
94
95     /**
96      * Create a message from an already built envelope and/or
97      * SOAPContext. The envelope argument may be null.
98      * Call save() to generate the byte array.
99      */

100     public TransportMessage(String JavaDoc envelope, SOAPContext ctx,
101                             Hashtable headers)
102         throws IllegalArgumentException JavaDoc, MessagingException,
103                IOException, SOAPException {
104         this.envelope = envelope;
105         this.ctx = ctx;
106         if (headers != null)
107             this.headers = headers;
108         else
109             this.headers = new Hashtable();
110     }
111
112     /**
113      * Create a message from an InputStream. This reads the InputStream and
114      * stores it in a byte array.
115      * Call read() to extract the SOAPContext and SOAP
116      * envelope from the byte array.
117      */

118     public TransportMessage(InputStream is, int contentLength,
119                             String JavaDoc contentType, SOAPContext ctx,
120                             Hashtable headers)
121         throws IllegalArgumentException JavaDoc, MessagingException,
122                IOException, SOAPException {
123         if (headers != null)
124             this.headers = headers;
125         else
126             this.headers = new Hashtable();
127         this.ctx = ctx;
128         this.contentType = contentType;
129
130         if (contentLength < 0)
131             throw new SOAPException (Constants.FAULT_CODE_PROTOCOL,
132                                      "Content length must be specified.");
133
134         bytes = new byte[contentLength];
135         int offset = 0;
136         int bytesRead = 0;
137
138         // We're done reading when we get all the content OR when the stream
139
// returns a -1.
140
while ((offset < contentLength) && (bytesRead >= 0)) {
141             bytesRead = is.read(bytes, offset, contentLength - offset);
142             offset += bytesRead;
143         }
144         if (offset < contentLength)
145             throw new SOAPException (Constants.FAULT_CODE_PROTOCOL,
146                        "Premature end of stream. Data is truncated. Read "
147                        + offset + " bytes successfully, expected "
148                        + contentLength);
149     }
150
151     /**
152      * Apply envelope/root part editor on inbound message.
153      * Nothing will be done if the root part is not text.
154      * If it is, the Content-Type of the root part is preserved.
155      */

156     public void editIncoming(EnvelopeEditor editor)
157         throws SOAPException, IOException, MessagingException {
158         editEnvelope(editor, true);
159     }
160
161     /**
162      * Apply envelope/root part editor on outgoing message.
163      * Nothing will be done if the root part is not text.
164      * If it is, the Content-Type of the root part is preserved.
165      */

166     public void editOutgoing(EnvelopeEditor editor)
167         throws SOAPException, IOException, MessagingException {
168         editEnvelope(editor, false);
169     }
170
171     /*
172      * Implementation of envelope editor.
173      */

174     protected void editEnvelope(EnvelopeEditor editor, boolean isIncoming)
175         throws SOAPException, IOException, MessagingException {
176         if (editor != null) {
177             // Do nothing if the root part is not text.
178
if (getEnvelope() == null)
179                 return;
180             // Apply filter.
181
StringWriter tout = new StringWriter();
182             if (isIncoming)
183                 editor.editIncoming(getEnvelopeReader(), tout);
184             else
185                 editor.editOutgoing(getEnvelopeReader(), tout);
186             tout.flush();
187             envelope = tout.toString();
188         }
189     }
190
191     /**
192      * Interpret byte array and extract SOAPContext and SOAP envelope (as
193      * a String). Make sure content type is set before calling this.
194      * Note that in the Messaging scenario, the root type is not necessarily
195      * a SOAP envelope, or even text. If it is text, the text is returned and
196      * it is up to the invoker to check the root part's Content-Type
197      */

198     public String JavaDoc read()
199         throws IllegalArgumentException JavaDoc, MessagingException,
200         IOException, SOAPException {
201
202         // Parse and validate content type.
203
ContentType cType = null;
204         try {
205             // Hack since WebSphere puts ;; instead of just ;
206
int pos = contentType.indexOf( ";;" );
207             if ( pos != -1 )
208               contentType = contentType.substring(0,pos) +
209                             contentType.substring(pos+1) ;
210             cType = new ContentType(contentType);
211         } catch(ParseException pe) {
212         }
213         if (cType == null)
214             throw new SOAPException(Constants.FAULT_CODE_PROTOCOL,
215                                     "Missing content type.");
216         MimeBodyPart rootPart;
217         ContentType rootContentType;
218         byte[] rootBytes;
219         if (cType.match(Constants.HEADERVAL_CONTENT_TYPE_MULTIPART_PRIMARY +
220                         "/*")) {
221             // Parse multipart request.
222
ByteArrayDataSource ds = new ByteArrayDataSource(bytes,
223                                                              contentType);
224
225             // Parse multipart mime into parts.
226
ctx.readMultipart(ds);
227
228             // Find root part.
229
rootPart = ctx.getRootPart();
230             rootContentType = new ContentType(rootPart.getContentType());
231             ByteArrayDataSource bads = new ByteArrayDataSource(
232                 rootPart.getInputStream(), null);
233             rootBytes = bads.toByteArray();
234         } else {
235             rootBytes = bytes;
236             rootContentType = cType;
237             // Set the single part as the root part of SOAPContext.
238
ByteArrayDataSource ds = new ByteArrayDataSource(bytes,
239                                                              contentType);
240             DataHandler dh = new DataHandler(ds);
241             rootPart = new MimeBodyPart();
242             rootPart.setDataHandler(dh);
243             ctx.addBodyPart(rootPart);
244         }
245
246         // If the root part is text, extract it as a String.
247
// Note that we could use JAF's help to do this (see getEnvelope())
248
// but implementing it ourselves is safer and faster.
249
if (rootContentType.match("text/*")) {
250             String JavaDoc charset = rootContentType.getParameter("charset");
251             // Hmm, risky, the default charset is transport-specific...
252
if (charset == null || charset.equals(""))
253                 charset = Constants.HEADERVAL_DEFAULT_CHARSET;
254             envelope = new String JavaDoc(rootBytes, MimeUtility.javaCharset(charset));
255         }
256
257         return envelope;
258     }
259
260     /**
261      * Parse envelope.
262      */

263     public Envelope unmarshall(DocumentBuilder xdb)
264         throws SOAPException {
265         Document doc;
266         try {
267             doc = xdb.parse(new InputSource(getEnvelopeReader()));
268         } catch(Exception JavaDoc e) {
269             throw new SOAPException(Constants.FAULT_CODE_CLIENT,
270                                     "parsing error: " + e);
271         }
272         if (doc == null) {
273             throw new SOAPException(Constants.FAULT_CODE_CLIENT,
274                                     "parsing error: received empty document");
275         }
276
277         return Envelope.unmarshall(doc.getDocumentElement());
278     }
279
280     /**
281      * Write message to byte array. Override this method for
282      * transport types that encode in a non-standard way.
283      */

284     public void save()
285         throws IllegalArgumentException JavaDoc, MessagingException, IOException {
286         /* If an envelope was provided as a string, set it as the root part.
287          * Otherwise, assume that the SOAPContext already has a root part.
288          * If there was already a root part, preserve its content-type.
289          */

290         String JavaDoc rootContentType = null;
291         if (ctx.isRootPartSet()) {
292             MimeBodyPart rootPart = ctx.getRootPart();
293             if (rootPart != null)
294                 // Note: don't call MimeBodyPart.getContent() because it will
295
// default to "text/plain" if the Content-Type header isn't
296
// set.
297
rootContentType = rootPart.getHeader(
298                     Constants.HEADER_CONTENT_TYPE, null);
299         }
300         if (rootContentType == null)
301             rootContentType = Constants.HEADERVAL_CONTENT_TYPE_UTF8;
302         if (getEnvelope() != null)
303             ctx.setRootPart(envelope, rootContentType);
304
305         // Print the whole response to a byte array.
306
ByteArrayOutputStream payload =
307             new ByteArrayOutputStream();
308         ctx.writeTo(payload);
309         bytes = payload.toByteArray();
310
311         // Now strip off the headers. (Grmbl, get rid of JavaMail
312
// for MIME support). Just intercept the Content-Type
313
// header. We don't want any of the MIME headers, and we know the
314
// overall Content-Length anyway.
315
StringBuffer JavaDoc namebuf = new StringBuffer JavaDoc();
316         StringBuffer JavaDoc valuebuf = new StringBuffer JavaDoc();
317         boolean parsingName = true;
318         for (offset = 0; offset < bytes.length; offset++) {
319             if (bytes[offset] == '\n') {
320                 // JavaMail sometimes word-wraps header parameters to the
321
// next line. Throttle through that.
322
if ((bytes[offset + 1] == ' ')
323                     || (bytes[offset + 1] == '\t')) {
324                     while ((bytes[(++offset) + 1] == ' ')
325                            || (bytes[offset + 1] == '\t'));
326                     continue;
327                 }
328                 if (namebuf.length() == 0) {
329                     offset++;
330                     break;
331                 }
332                 String JavaDoc name = namebuf.toString();
333                 // For multipart, append type and start parameters to
334
// Content-Type header.
335
if (name.equals(Constants.HEADER_CONTENT_TYPE)) {
336                     contentType = valuebuf.toString();
337                     if (ctx.getCount() > 1) {
338                         String JavaDoc rootCID = ctx.getRootPart().getContentID();
339                         // Strip < and > off Content-ID.
340
rootCID = rootCID.substring(1, rootCID.length() - 1);
341                         contentType += "; type=\""
342                             + Constants.HEADERVAL_CONTENT_TYPE
343                             + "\"; start=\"" + rootCID + '"';
344                     }
345                 }
346                 namebuf = new StringBuffer JavaDoc();
347                 valuebuf = new StringBuffer JavaDoc();
348                 parsingName = true;
349                 }
350             else if (bytes[offset] != '\r') {
351                 if (parsingName) {
352                     if (bytes[offset] == ':') {
353                         parsingName = false;
354                         offset++;
355                     }
356                     else
357                         namebuf.append((char)bytes[offset]);
358                 }
359                 else
360                         valuebuf.append((char)bytes[offset]);
361             }
362         }
363     }
364
365     /**
366      * Get SOAPContext.
367      */

368     public SOAPContext getSOAPContext() {
369         return ctx;
370     }
371
372     /**
373      * Get SOAP Envelope/root part as a String.
374      * This method will extract the root part from the SOAPContext as a String
375      * if there is no SOAP Envelope.
376      */

377     public String JavaDoc getEnvelope() throws MessagingException, IOException {
378         if (envelope == null) {
379             MimeBodyPart rootPart = ctx.getRootPart();
380             if (rootPart != null)
381                 if (rootPart.isMimeType("text/*")) {
382                     ByteArrayDataSource ds = new ByteArrayDataSource(
383                         rootPart.getInputStream(), rootPart.getContentType());
384                     envelope = ds.getText();
385             }
386         }
387         return envelope;
388     }
389
390     /**
391      * Get SOAP Envelope/root part as a Reader. Returns null if the root part
392      * is not text.
393      */

394     public Reader getEnvelopeReader() throws MessagingException, IOException {
395         if (getEnvelope() == null)
396             return null;
397         else
398             return new StringReader(envelope);
399     }
400
401     /**
402      * Set SOAP Envelope.
403      */

404     public void setEnvelope(String JavaDoc envelope) {
405         this.envelope = envelope;
406     }
407
408     /**
409      * Get Content-Type.
410      */

411     public String JavaDoc getContentType() {
412         return contentType;
413     }
414
415     /**
416      * Set Content-Type as String.
417      */

418     public void setContentType(String JavaDoc contentType) {
419         this.contentType = contentType;
420     }
421
422     /**
423      * Get size of response content in bytes.
424      */

425     public int getContentLength() {
426         return bytes.length - offset;
427     }
428
429     /**
430      * Set a transport header.
431      */

432     public void setHeader(String JavaDoc name, String JavaDoc value) {
433         headers.put(name, value);
434     }
435
436     /**
437      * Get a transport header.
438      */

439     public String JavaDoc getHeader(String JavaDoc name) {
440         return (String JavaDoc)headers.get(name);
441     }
442
443     /**
444      * Get transport header names.
445      */

446     public Enumeration getHeaderNames() {
447         return headers.keys();
448     }
449
450     /**
451      * Get the complete header hashtable.
452      */

453     public Hashtable getHeaders() {
454         return headers;
455     }
456
457     /**
458      * Write content.
459      */

460     public void writeTo(OutputStream outStream) throws IOException {
461         outStream.write(bytes, offset, bytes.length - offset);
462         outStream.flush();
463     }
464
465     /**
466      * Set the byte array of the response.
467      */

468     public void setBytes(byte[] data) {
469         offset = 0;
470         bytes = data;
471     }
472
473     /**
474      * Set the byte array of the response.
475      */

476     public void readFully(InputStream is) throws IOException {
477         offset = 0;
478         ByteArrayDataSource bads = new ByteArrayDataSource(is, null);
479         bytes = bads.toByteArray();
480     }
481
482     /**
483      * Get the response byte array.
484      */

485     public byte[] getBytes() {
486         // The offset trick is an efficiency hack. Eliminate that here.
487
if (offset != 0) {
488             byte[] data = new byte[bytes.length - offset];
489             System.arraycopy(bytes, offset, data, 0, data.length);
490             bytes = data;
491             offset = 0;
492         }
493         return bytes;
494     }
495 }
496
Popular Tags