1 /* |
|
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package com.sun.xml.internal.messaging.saaj.soap; |
|
27 |
|
28 import java.io.*; |
|
29 import java.util.*; |
|
30 import java.util.logging.Level; |
|
31 import java.util.logging.Logger; |
|
32 |
|
33 import javax.activation.DataHandler; |
|
34 import javax.activation.DataSource; |
|
35 import javax.xml.soap.*; |
|
36 import javax.xml.stream.XMLStreamReader; |
|
37 import javax.xml.transform.Source; |
|
38 import javax.xml.transform.stax.StAXSource; |
|
39 import javax.xml.transform.stream.StreamSource; |
|
40 |
|
41 import com.sun.xml.internal.messaging.saaj.packaging.mime.Header; |
|
42 import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.*; |
|
43 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*; |
|
44 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException; |
|
45 |
|
46 import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl; |
|
47 import com.sun.xml.internal.messaging.saaj.soap.impl.EnvelopeImpl; |
|
48 import com.sun.xml.internal.messaging.saaj.util.*; |
|
49 import com.sun.xml.internal.org.jvnet.mimepull.MIMEPart; |
|
50 |
|
51 /** |
|
52 * The message implementation for SOAP messages with |
|
53 * attachments. Messages for specific profiles will likely extend this |
|
54 * MessageImpl class and add more value for that particular profile. |
|
55 * |
|
56 * @author Anil Vijendran (akv@eng.sun.com) |
|
57 * @author Rajiv Mordani (rajiv.mordani@sun.com) |
|
58 * @author Manveen Kaur (manveen.kaur@sun.com) |
|
59 */ |
|
60 |
|
61 public abstract class MessageImpl |
|
62 extends SOAPMessage |
|
63 implements SOAPConstants { |
|
64 |
|
65 |
|
66 public static final String CONTENT_ID = "Content-ID"; |
|
67 public static final String CONTENT_LOCATION = "Content-Location"; |
|
68 |
|
69 protected static final Logger log = |
|
70 Logger.getLogger(LogDomainConstants.SOAP_DOMAIN, |
|
71 "com.sun.xml.internal.messaging.saaj.soap.LocalStrings"); |
|
72 |
|
73 protected static final int PLAIN_XML_FLAG = 1; // 00001 |
|
74 protected static final int MIME_MULTIPART_FLAG = 2; // 00010 |
|
75 protected static final int SOAP1_1_FLAG = 4; // 00100 |
|
76 protected static final int SOAP1_2_FLAG = 8; // 01000 |
|
77 //protected static final int MIME_MULTIPART_XOP_FLAG = 14; // 01110 |
|
78 protected static final int MIME_MULTIPART_XOP_SOAP1_1_FLAG = 6; // 00110 |
|
79 protected static final int MIME_MULTIPART_XOP_SOAP1_2_FLAG = 10; // 01010 |
|
80 protected static final int XOP_FLAG = 13; // 01101 |
|
81 protected static final int FI_ENCODED_FLAG = 16; // 10000 |
|
82 |
|
83 protected MimeHeaders headers; |
|
84 protected ContentType contentType; |
|
85 protected SOAPPartImpl soapPartImpl; |
|
86 protected FinalArrayList attachments; |
|
87 protected boolean saved = false; |
|
88 protected byte[] messageBytes; |
|
89 protected int messageByteCount; |
|
90 protected HashMap properties = new HashMap(); |
|
91 |
|
92 // used for lazy attachment initialization |
|
93 protected MimeMultipart multiPart = null; |
|
94 protected boolean attachmentsInitialized = false; |
|
95 |
|
96 /** |
|
97 * True if this part is encoded using Fast Infoset. |
|
98 * MIME -> application/fastinfoset |
|
99 */ |
|
100 protected boolean isFastInfoset = false; |
|
101 |
|
102 /** |
|
103 * True if the Accept header of this message includes |
|
104 * application/fastinfoset |
|
105 */ |
|
106 protected boolean acceptFastInfoset = false; |
|
107 |
|
108 protected MimeMultipart mmp = null; |
|
109 |
|
110 // if attachments are present, don't read the entire message in byte stream in saveTo() |
|
111 private boolean optimizeAttachmentProcessing = true; |
|
112 |
|
113 private InputStream inputStreamAfterSaveChanges = null; |
|
114 |
|
115 public static final String LAZY_SOAP_BODY_PARSING = "saaj.lazy.soap.body"; |
|
116 |
|
117 // switch back to old MimeMultipart incase of problem |
|
118 private static boolean switchOffBM = false; |
|
119 private static boolean switchOffLazyAttachment = false; |
|
120 private static boolean useMimePull = false; |
|
121 |
|
122 static { |
|
123 String s = SAAJUtil.getSystemProperty("saaj.mime.optimization"); |
|
124 if ((s != null) && s.equals("false")) { |
|
125 switchOffBM = true; |
|
126 } |
|
127 s = SAAJUtil.getSystemProperty("saaj.lazy.mime.optimization"); |
|
128 if ((s != null) && s.equals("false")) { |
|
129 switchOffLazyAttachment = true; |
|
130 } |
|
131 useMimePull = SAAJUtil.getSystemBoolean("saaj.use.mimepull"); |
|
132 |
|
133 } |
|
134 |
|
135 //property to indicate optimized serialization for lazy attachments |
|
136 private boolean lazyAttachments = false; |
|
137 |
|
138 // most of the times, Content-Types are already all lower cased. |
|
139 // String.toLowerCase() works faster in this case, so even if you |
|
140 // are only doing one comparison, it pays off to use String.toLowerCase() |
|
141 // than String.equalsIgnoreCase(). When you do more than one comparison, |
|
142 // the benefits of String.toLowerCase() dominates. |
|
143 // |
|
144 // |
|
145 // for FI, |
|
146 // use application/fastinfoset for SOAP 1.1 |
|
147 // use application/soap+fastinfoset for SOAP 1.2 |
|
148 // to speed up comparisons, test methods always use lower cases. |
|
149 |
|
150 /** |
|
151 * @param primary |
|
152 * must be all lower case |
|
153 * @param sub |
|
154 * must be all lower case |
|
155 */ |
|
156 private static boolean isSoap1_1Type(String primary, String sub) { |
|
157 return primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml") |
|
158 || primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml-soap") |
|
159 || primary.equals("application") |
|
160 && sub.equals("fastinfoset"); |
|
161 } |
|
162 |
|
163 /** |
|
164 * @param type |
|
165 * must be all lower case |
|
166 */ |
|
167 private static boolean isEqualToSoap1_1Type(String type) { |
|
168 return type.startsWith("text/xml") || |
|
169 type.startsWith("application/fastinfoset"); |
|
170 } |
|
171 |
|
172 /** |
|
173 * @param primary |
|
174 * must be all lower case |
|
175 * @param sub |
|
176 * must be all lower case |
|
177 */ |
|
178 private static boolean isSoap1_2Type(String primary, String sub) { |
|
179 return primary.equals("application") |
|
180 && (sub.equals("soap+xml") |
|
181 || sub.equals("soap+fastinfoset")); |
|
182 } |
|
183 |
|
184 /** |
|
185 * @param type |
|
186 * must be all lower case |
|
187 */ |
|
188 private static boolean isEqualToSoap1_2Type(String type) { |
|
189 return type.startsWith("application/soap+xml") || |
|
190 type.startsWith("application/soap+fastinfoset"); |
|
191 } |
|
192 |
|
193 /** |
|
194 * Construct a new message. This will be invoked before message |
|
195 * sends. |
|
196 */ |
|
197 protected MessageImpl() { |
|
198 this(false, false); |
|
199 attachmentsInitialized = true; |
|
200 } |
|
201 |
|
202 /** |
|
203 * Construct a new message. This will be invoked before message |
|
204 * sends. |
|
205 */ |
|
206 protected MessageImpl(boolean isFastInfoset, boolean acceptFastInfoset) { |
|
207 this.isFastInfoset = isFastInfoset; |
|
208 this.acceptFastInfoset = acceptFastInfoset; |
|
209 |
|
210 headers = new MimeHeaders(); |
|
211 headers.setHeader("Accept", getExpectedAcceptHeader()); |
|
212 contentType = new ContentType(); |
|
213 } |
|
214 |
|
215 /** |
|
216 * Shallow copy. |
|
217 */ |
|
218 protected MessageImpl(SOAPMessage msg) { |
|
219 if (!(msg instanceof MessageImpl)) { |
|
220 // don't know how to handle this. |
|
221 } |
|
222 MessageImpl src = (MessageImpl) msg; |
|
223 this.headers = src.headers; |
|
224 this.soapPartImpl = src.soapPartImpl; |
|
225 this.attachments = src.attachments; |
|
226 this.saved = src.saved; |
|
227 this.messageBytes = src.messageBytes; |
|
228 this.messageByteCount = src.messageByteCount; |
|
229 this.properties = src.properties; |
|
230 this.contentType = src.contentType; |
|
231 } |
|
232 |
|
233 /** |
|
234 * @param stat |
|
235 * the mask value obtained from {@link #identifyContentType(ContentType)} |
|
236 */ |
|
237 protected static boolean isSoap1_1Content(int stat) { |
|
238 return (stat & SOAP1_1_FLAG) != 0; |
|
239 } |
|
240 |
|
241 /** |
|
242 * @param stat |
|
243 * the mask value obtained from {@link #identifyContentType(ContentType)} |
|
244 */ |
|
245 protected static boolean isSoap1_2Content(int stat) { |
|
246 return (stat & SOAP1_2_FLAG) != 0; |
|
247 } |
|
248 |
|
249 private static boolean isMimeMultipartXOPSoap1_2Package(ContentType contentType) { |
|
250 String type = contentType.getParameter("type"); |
|
251 if (type == null) { |
|
252 return false; |
|
253 } |
|
254 type = type.toLowerCase(); |
|
255 if (!type.startsWith("application/xop+xml")) { |
|
256 return false; |
|
257 } |
|
258 String startinfo = contentType.getParameter("start-info"); |
|
259 if (startinfo == null) { |
|
260 return false; |
|
261 } |
|
262 startinfo = startinfo.toLowerCase(); |
|
263 return isEqualToSoap1_2Type(startinfo); |
|
264 } |
|
265 |
|
266 |
|
267 //private static boolean isMimeMultipartXOPPackage(ContentType contentType) { |
|
268 private static boolean isMimeMultipartXOPSoap1_1Package(ContentType contentType) { |
|
269 String type = contentType.getParameter("type"); |
|
270 if(type==null) |
|
271 return false; |
|
272 |
|
273 type = type.toLowerCase(); |
|
274 if(!type.startsWith("application/xop+xml")) |
|
275 return false; |
|
276 |
|
277 String startinfo = contentType.getParameter("start-info"); |
|
278 if(startinfo == null) |
|
279 return false; |
|
280 startinfo = startinfo.toLowerCase(); |
|
281 return isEqualToSoap1_1Type(startinfo); |
|
282 } |
|
283 |
|
284 private static boolean isSOAPBodyXOPPackage(ContentType contentType){ |
|
285 String primary = contentType.getPrimaryType(); |
|
286 String sub = contentType.getSubType(); |
|
287 |
|
288 if (primary.equalsIgnoreCase("application")) { |
|
289 if (sub.equalsIgnoreCase("xop+xml")) { |
|
290 String type = getTypeParameter(contentType); |
|
291 return isEqualToSoap1_2Type(type) || isEqualToSoap1_1Type(type); |
|
292 } |
|
293 } |
|
294 return false; |
|
295 } |
|
296 |
|
297 /** |
|
298 * Construct a message from an input stream. When messages are |
|
299 * received, there's two parts -- the transport headers and the |
|
300 * message content in a transport specific stream. |
|
301 */ |
|
302 protected MessageImpl(MimeHeaders headers, final InputStream in) |
|
303 throws SOAPExceptionImpl { |
|
304 contentType = parseContentType(headers); |
|
305 init(headers,identifyContentType(contentType),contentType,in); |
|
306 } |
|
307 |
|
308 private static ContentType parseContentType(MimeHeaders headers) throws SOAPExceptionImpl { |
|
309 final String ct; |
|
310 if (headers != null) |
|
311 ct = getContentType(headers); |
|
312 else { |
|
313 log.severe("SAAJ0550.soap.null.headers"); |
|
314 throw new SOAPExceptionImpl("Cannot create message: " + |
|
315 "Headers can't be null"); |
|
316 } |
|
317 |
|
318 if (ct == null) { |
|
319 log.severe("SAAJ0532.soap.no.Content-Type"); |
|
320 throw new SOAPExceptionImpl("Absent Content-Type"); |
|
321 } |
|
322 try { |
|
323 return new ContentType(ct); |
|
324 } catch (Throwable ex) { |
|
325 log.severe("SAAJ0535.soap.cannot.internalize.message"); |
|
326 throw new SOAPExceptionImpl("Unable to internalize message", ex); |
|
327 } |
|
328 } |
|
329 |
|
330 /** |
|
331 * Construct a message from an input stream. When messages are |
|
332 * received, there's two parts -- the transport headers and the |
|
333 * message content in a transport specific stream. |
|
334 * |
|
335 * @param contentType |
|
336 * The parsed content type header from the headers variable. |
|
337 * This is redundant parameter, but it avoids reparsing this header again. |
|
338 * @param stat |
|
339 * The result of {@link #identifyContentType(ContentType)} over |
|
340 * the contentType parameter. This redundant parameter, but it avoids |
|
341 * recomputing this information again. |
|
342 */ |
|
343 protected MessageImpl(MimeHeaders headers, final ContentType contentType, int stat, final InputStream in) throws SOAPExceptionImpl { |
|
344 init(headers, stat, contentType, in); |
|
345 |
|
346 } |
|
347 |
|
348 public MessageImpl(MimeHeaders headers, ContentType ct, int stat, |
|
349 XMLStreamReader reader) throws SOAPExceptionImpl { |
|
350 init(headers, stat, ct, reader); |
|
351 } |
|
352 |
|
353 private void init(MimeHeaders headers, int stat, final ContentType contentType, final Object input) throws SOAPExceptionImpl { |
|
354 this.headers = headers; |
|
355 |
|
356 try { |
|
357 |
|
358 // Set isFastInfoset/acceptFastInfoset flag based on MIME type |
|
359 if ((stat & FI_ENCODED_FLAG) > 0) { |
|
360 isFastInfoset = acceptFastInfoset = true; |
|
361 } |
|
362 |
|
363 // If necessary, inspect Accept header to set acceptFastInfoset |
|
364 if (!isFastInfoset) { |
|
365 String[] values = headers.getHeader("Accept"); |
|
366 if (values != null) { |
|
367 for (int i = 0; i < values.length; i++) { |
|
368 StringTokenizer st = new StringTokenizer(values[i], ","); |
|
369 while (st.hasMoreTokens()) { |
|
370 final String token = st.nextToken().trim(); |
|
371 if (token.equalsIgnoreCase("application/fastinfoset") || |
|
372 token.equalsIgnoreCase("application/soap+fastinfoset")) { |
|
373 acceptFastInfoset = true; |
|
374 break; |
|
375 } |
|
376 } |
|
377 } |
|
378 } |
|
379 } |
|
380 |
|
381 if (!isCorrectSoapVersion(stat)) { |
|
382 log.log( |
|
383 Level.SEVERE, |
|
384 "SAAJ0533.soap.incorrect.Content-Type", |
|
385 new String[] { |
|
386 contentType.toString(), |
|
387 getExpectedContentType()}); |
|
388 throw new SOAPVersionMismatchException( |
|
389 "Cannot create message: incorrect content-type for SOAP version. Got: " |
|
390 + contentType |
|
391 + " Expected: " |
|
392 + getExpectedContentType()); |
|
393 } |
|
394 InputStream in = null; |
|
395 XMLStreamReader rdr = null; |
|
396 if (input instanceof InputStream) { |
|
397 in = (InputStream) input; |
|
398 } else { |
|
399 //is a StAX reader |
|
400 rdr = (XMLStreamReader) input; |
|
401 } |
|
402 if ((stat & PLAIN_XML_FLAG) != 0) { |
|
403 if (in != null) { |
|
404 if (isFastInfoset) { |
|
405 getSOAPPart().setContent( |
|
406 FastInfosetReflection.FastInfosetSource_new(in)); |
|
407 } else { |
|
408 initCharsetProperty(contentType); |
|
409 getSOAPPart().setContent(new StreamSource(in)); |
|
410 } |
|
411 } else { |
|
412 //is a StAX reader |
|
413 if (isFastInfoset) { |
|
414 //need to get FI stax reader |
|
415 } else { |
|
416 initCharsetProperty(contentType); |
|
417 getSOAPPart().setContent(new StAXSource(rdr)); |
|
418 } |
|
419 } |
|
420 } |
|
421 else if ((stat & MIME_MULTIPART_FLAG) != 0 && in == null) { |
|
422 //only parse multipart in the inputstream case |
|
423 //in stax reader case, we would be given the attachments separately |
|
424 getSOAPPart().setContent(new StAXSource(rdr)); |
|
425 } else if ((stat & MIME_MULTIPART_FLAG) != 0) { |
|
426 final InputStream finalIn = in; |
|
427 DataSource ds = new DataSource() { |
|
428 public InputStream getInputStream() { |
|
429 return finalIn; |
|
430 } |
|
431 |
|
432 public OutputStream getOutputStream() { |
|
433 return null; |
|
434 } |
|
435 |
|
436 public String getContentType() { |
|
437 return contentType.toString(); |
|
438 } |
|
439 |
|
440 public String getName() { |
|
441 return ""; |
|
442 } |
|
443 }; |
|
444 |
|
445 multiPart = null; |
|
446 if (useMimePull) { |
|
447 multiPart = new MimePullMultipart(ds,contentType); |
|
448 } else if (switchOffBM) { |
|
449 multiPart = new MimeMultipart(ds,contentType); |
|
450 } else { |
|
451 multiPart = new BMMimeMultipart(ds,contentType); |
|
452 } |
|
453 |
|
454 String startParam = contentType.getParameter("start"); |
|
455 MimeBodyPart soapMessagePart = null; |
|
456 InputStream soapPartInputStream = null; |
|
457 String contentID = null; |
|
458 String contentIDNoAngle = null; |
|
459 if (switchOffBM || switchOffLazyAttachment) { |
|
460 if(startParam == null) { |
|
461 soapMessagePart = multiPart.getBodyPart(0); |
|
462 for (int i = 1; i < multiPart.getCount(); i++) { |
|
463 initializeAttachment(multiPart, i); |
|
464 } |
|
465 } else { |
|
466 soapMessagePart = multiPart.getBodyPart(startParam); |
|
467 for (int i = 0; i < multiPart.getCount(); i++) { |
|
468 contentID = multiPart.getBodyPart(i).getContentID(); |
|
469 // Old versions of AXIS2 put angle brackets around the content |
|
470 // id but not the start param |
|
471 contentIDNoAngle = (contentID != null) ? |
|
472 contentID.replaceFirst("^<", "").replaceFirst(">$", "") : null; |
|
473 if(!startParam.equals(contentID) && !startParam.equals(contentIDNoAngle)) |
|
474 initializeAttachment(multiPart, i); |
|
475 } |
|
476 } |
|
477 } else { |
|
478 if (useMimePull) { |
|
479 MimePullMultipart mpMultipart = (MimePullMultipart)multiPart; |
|
480 MIMEPart sp = mpMultipart.readAndReturnSOAPPart(); |
|
481 soapMessagePart = new MimeBodyPart(sp); |
|
482 soapPartInputStream = sp.readOnce(); |
|
483 } else { |
|
484 BMMimeMultipart bmMultipart = |
|
485 (BMMimeMultipart) multiPart; |
|
486 InputStream stream = bmMultipart.initStream(); |
|
487 |
|
488 SharedInputStream sin = null; |
|
489 if (stream instanceof SharedInputStream) { |
|
490 sin = (SharedInputStream) stream; |
|
491 } |
|
492 |
|
493 String boundary = "--" + |
|
494 contentType.getParameter("boundary"); |
|
495 byte[] bndbytes = ASCIIUtility.getBytes(boundary); |
|
496 if (startParam == null) { |
|
497 soapMessagePart = |
|
498 bmMultipart.getNextPart(stream, bndbytes, sin); |
|
499 bmMultipart.removeBodyPart(soapMessagePart); |
|
500 } else { |
|
501 MimeBodyPart bp = null; |
|
502 try { |
|
503 while (!startParam.equals(contentID) && !startParam.equals(contentIDNoAngle)) { |
|
504 bp = bmMultipart.getNextPart( |
|
505 stream, bndbytes, sin); |
|
506 contentID = bp.getContentID(); |
|
507 // Old versions of AXIS2 put angle brackets around the content |
|
508 // id but not the start param |
|
509 contentIDNoAngle = (contentID != null) ? |
|
510 contentID.replaceFirst("^<", "").replaceFirst(">$", "") : null; |
|
511 } |
|
512 soapMessagePart = bp; |
|
513 bmMultipart.removeBodyPart(bp); |
|
514 } catch (Exception e) { |
|
515 throw new SOAPExceptionImpl(e); |
|
516 } |
|
517 } |
|
518 } |
|
519 } |
|
520 |
|
521 // findbugs correctly points out that we'd NPE instantiating |
|
522 // the ContentType (just below here) if soapMessagePart were |
|
523 // null. Hence are better off throwing a controlled exception |
|
524 // at this point if it is null. |
|
525 if (soapMessagePart == null) { |
|
526 log.severe("SAAJ0510.soap.cannot.create.envelope"); |
|
527 throw new SOAPExceptionImpl( |
|
528 "Unable to create envelope from given source: SOAP part not found"); |
|
529 } |
|
530 |
|
531 if (soapPartInputStream == null) { |
|
532 soapPartInputStream = soapMessagePart.getInputStream(); |
|
533 } |
|
534 |
|
535 ContentType soapPartCType = new ContentType( |
|
536 soapMessagePart.getContentType()); |
|
537 initCharsetProperty(soapPartCType); |
|
538 String baseType = soapPartCType.getBaseType().toLowerCase(); |
|
539 if(!(isEqualToSoap1_1Type(baseType) |
|
540 || isEqualToSoap1_2Type(baseType) |
|
541 || isSOAPBodyXOPPackage(soapPartCType))) { |
|
542 log.log(Level.SEVERE, |
|
543 "SAAJ0549.soap.part.invalid.Content-Type", |
|
544 new Object[] {baseType}); |
|
545 throw new SOAPExceptionImpl( |
|
546 "Bad Content-Type for SOAP Part : " + |
|
547 baseType); |
|
548 } |
|
549 |
|
550 SOAPPart soapPart = getSOAPPart(); |
|
551 setMimeHeaders(soapPart, soapMessagePart); |
|
552 soapPart.setContent(isFastInfoset ? |
|
553 (Source) FastInfosetReflection.FastInfosetSource_new( |
|
554 soapPartInputStream) : |
|
555 (Source) new StreamSource(soapPartInputStream)); |
|
556 } else { |
|
557 log.severe("SAAJ0534.soap.unknown.Content-Type"); |
|
558 throw new SOAPExceptionImpl("Unrecognized Content-Type"); |
|
559 } |
|
560 } catch (Throwable ex) { |
|
561 log.severe("SAAJ0535.soap.cannot.internalize.message"); |
|
562 throw new SOAPExceptionImpl("Unable to internalize message", ex); |
|
563 } |
|
564 needsSave(); |
|
565 } |
|
566 |
|
567 public boolean isFastInfoset() { |
|
568 return isFastInfoset; |
|
569 } |
|
570 |
|
571 public boolean acceptFastInfoset() { |
|
572 return acceptFastInfoset; |
|
573 } |
|
574 |
|
575 public void setIsFastInfoset(boolean value) { |
|
576 if (value != isFastInfoset) { |
|
577 isFastInfoset = value; |
|
578 if (isFastInfoset) { |
|
579 acceptFastInfoset = true; |
|
580 } |
|
581 saved = false; // ensure transcoding if necessary |
|
582 } |
|
583 } |
|
584 |
|
585 public boolean isLazySoapBodyParsing() { |
|
586 Object lazyParsingProp = getProperty(LAZY_SOAP_BODY_PARSING); |
|
587 if (lazyParsingProp == null) return false; |
|
588 if (lazyParsingProp instanceof Boolean) { |
|
589 return ((Boolean) lazyParsingProp).booleanValue(); |
|
590 } else { |
|
591 return Boolean.valueOf(lazyParsingProp.toString()); |
|
592 } |
|
593 } |
|
594 public Object getProperty(String property) { |
|
595 return (String) properties.get(property); |
|
596 } |
|
597 |
|
598 public void setProperty(String property, Object value) { |
|
599 verify(property, value); |
|
600 properties.put(property, value); |
|
601 } |
|
602 |
|
603 private void verify(String property, Object value) { |
|
604 if (property.equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) { |
|
605 if (!("true".equals(value) || "false".equals(value))) |
|
606 throw new RuntimeException( |
|
607 property + " must have value false or true"); |
|
608 |
|
609 try { |
|
610 EnvelopeImpl env = (EnvelopeImpl) getSOAPPart().getEnvelope(); |
|
611 if ("true".equalsIgnoreCase((String)value)) { |
|
612 env.setOmitXmlDecl("no"); |
|
613 } else if ("false".equalsIgnoreCase((String)value)) { |
|
614 env.setOmitXmlDecl("yes"); |
|
615 } |
|
616 } catch (Exception e) { |
|
617 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property", |
|
618 new Object[] {e.getMessage(), "javax.xml.soap.write-xml-declaration"}); |
|
619 throw new RuntimeException(e); |
|
620 } |
|
621 return; |
|
622 } |
|
623 |
|
624 if (property.equalsIgnoreCase(SOAPMessage.CHARACTER_SET_ENCODING)) { |
|
625 try { |
|
626 ((EnvelopeImpl) getSOAPPart().getEnvelope()).setCharsetEncoding((String)value); |
|
627 } catch (Exception e) { |
|
628 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property", |
|
629 new Object[] {e.getMessage(), "javax.xml.soap.character-set-encoding"}); |
|
630 throw new RuntimeException(e); |
|
631 } |
|
632 } |
|
633 } |
|
634 |
|
635 protected abstract boolean isCorrectSoapVersion(int contentTypeId); |
|
636 |
|
637 protected abstract String getExpectedContentType(); |
|
638 protected abstract String getExpectedAcceptHeader(); |
|
639 |
|
640 /** |
|
641 * Sniffs the Content-Type header so that we can determine how to process. |
|
642 * |
|
643 * <p> |
|
644 * In the absence of type attribute we assume it to be text/xml. |
|
645 * That would mean we're easy on accepting the message and |
|
646 * generate the correct thing (as the SWA spec also specifies |
|
647 * that the type parameter should always be text/xml) |
|
648 * |
|
649 * @return |
|
650 * combination of flags, such as PLAIN_XML_CODE and MIME_MULTIPART_CODE. |
|
651 */ |
|
652 // SOAP1.2 allow SOAP1.2 content type |
|
653 static int identifyContentType(ContentType ct) |
|
654 throws SOAPExceptionImpl { |
|
655 // TBD |
|
656 // Is there anything else we need to verify here? |
|
657 |
|
658 String primary = ct.getPrimaryType().toLowerCase(); |
|
659 String sub = ct.getSubType().toLowerCase(); |
|
660 |
|
661 if (primary.equals("multipart")) { |
|
662 if (sub.equals("related")) { |
|
663 String type = getTypeParameter(ct); |
|
664 if (isEqualToSoap1_1Type(type)) { |
|
665 return (type.equals("application/fastinfoset") ? |
|
666 FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_1_FLAG; |
|
667 } |
|
668 else if (isEqualToSoap1_2Type(type)) { |
|
669 return (type.equals("application/soap+fastinfoset") ? |
|
670 FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_2_FLAG; |
|
671 /*} else if (isMimeMultipartXOPPackage(ct)) { |
|
672 return MIME_MULTIPART_XOP_FLAG;*/ |
|
673 } else if (isMimeMultipartXOPSoap1_1Package(ct)) { |
|
674 return MIME_MULTIPART_XOP_SOAP1_1_FLAG; |
|
675 } else if (isMimeMultipartXOPSoap1_2Package(ct)) { |
|
676 return MIME_MULTIPART_XOP_SOAP1_2_FLAG; |
|
677 } else { |
|
678 log.severe("SAAJ0536.soap.content-type.mustbe.multipart"); |
|
679 throw new SOAPExceptionImpl( |
|
680 "Content-Type needs to be Multipart/Related " |
|
681 + "and with \"type=text/xml\" " |
|
682 + "or \"type=application/soap+xml\""); |
|
683 } |
|
684 } else { |
|
685 log.severe("SAAJ0537.soap.invalid.content-type"); |
|
686 throw new SOAPExceptionImpl( |
|
687 "Invalid Content-Type: " + primary + '/' + sub); |
|
688 } |
|
689 } |
|
690 else if (isSoap1_1Type(primary, sub)) { |
|
691 return (primary.equalsIgnoreCase("application") |
|
692 && sub.equalsIgnoreCase("fastinfoset") ? |
|
693 FI_ENCODED_FLAG : 0) |
|
694 | PLAIN_XML_FLAG | SOAP1_1_FLAG; |
|
695 } |
|
696 else if (isSoap1_2Type(primary, sub)) { |
|
697 return (primary.equalsIgnoreCase("application") |
|
698 && sub.equalsIgnoreCase("soap+fastinfoset") ? |
|
699 FI_ENCODED_FLAG : 0) |
|
700 | PLAIN_XML_FLAG | SOAP1_2_FLAG; |
|
701 } else if(isSOAPBodyXOPPackage(ct)){ |
|
702 return XOP_FLAG; |
|
703 } else { |
|
704 log.severe("SAAJ0537.soap.invalid.content-type"); |
|
705 throw new SOAPExceptionImpl( |
|
706 "Invalid Content-Type:" |
|
707 + primary |
|
708 + '/' |
|
709 + sub |
|
710 + ". Is this an error message instead of a SOAP response?"); |
|
711 } |
|
712 } |
|
713 |
|
714 /** |
|
715 * Obtains the type parameter of the Content-Type header. Defaults to "text/xml". |
|
716 */ |
|
717 private static String getTypeParameter(ContentType contentType) { |
|
718 String p = contentType.getParameter("type"); |
|
719 if(p!=null) |
|
720 return p.toLowerCase(); |
|
721 else |
|
722 return "text/xml"; |
|
723 } |
|
724 |
|
725 public MimeHeaders getMimeHeaders() { |
|
726 return this.headers; |
|
727 } |
|
728 |
|
729 final static String getContentType(MimeHeaders headers) { |
|
730 String[] values = headers.getHeader("Content-Type"); |
|
731 if (values == null) |
|
732 return null; |
|
733 else |
|
734 return values[0]; |
|
735 } |
|
736 |
|
737 /* |
|
738 * Get the complete ContentType value along with optional parameters. |
|
739 */ |
|
740 public String getContentType() { |
|
741 return getContentType(this.headers); |
|
742 } |
|
743 |
|
744 public void setContentType(String type) { |
|
745 headers.setHeader("Content-Type", type); |
|
746 needsSave(); |
|
747 } |
|
748 |
|
749 private ContentType contentType() { |
|
750 ContentType ct = null; |
|
751 try { |
|
752 String currentContent = getContentType(); |
|
753 if (currentContent == null) { |
|
754 return this.contentType; |
|
755 } |
|
756 ct = new ContentType(currentContent); |
|
757 } catch (Exception e) { |
|
758 // what to do here? |
|
759 } |
|
760 return ct; |
|
761 } |
|
762 |
|
763 /* |
|
764 * Return the MIME type string, without the parameters. |
|
765 */ |
|
766 public String getBaseType() { |
|
767 return contentType().getBaseType(); |
|
768 } |
|
769 |
|
770 public void setBaseType(String type) { |
|
771 ContentType ct = contentType(); |
|
772 ct.setParameter("type", type); |
|
773 headers.setHeader("Content-Type", ct.toString()); |
|
774 needsSave(); |
|
775 } |
|
776 |
|
777 public String getAction() { |
|
778 return contentType().getParameter("action"); |
|
779 } |
|
780 |
|
781 public void setAction(String action) { |
|
782 ContentType ct = contentType(); |
|
783 ct.setParameter("action", action); |
|
784 headers.setHeader("Content-Type", ct.toString()); |
|
785 needsSave(); |
|
786 } |
|
787 |
|
788 public String getCharset() { |
|
789 return contentType().getParameter("charset"); |
|
790 } |
|
791 |
|
792 public void setCharset(String charset) { |
|
793 ContentType ct = contentType(); |
|
794 ct.setParameter("charset", charset); |
|
795 headers.setHeader("Content-Type", ct.toString()); |
|
796 needsSave(); |
|
797 } |
|
798 |
|
799 /** |
|
800 * All write methods (i.e setters) should call this method in |
|
801 * order to make sure that a save is necessary since the state |
|
802 * has been modified. |
|
803 */ |
|
804 private final void needsSave() { |
|
805 saved = false; |
|
806 } |
|
807 |
|
808 public boolean saveRequired() { |
|
809 return saved != true; |
|
810 } |
|
811 |
|
812 public String getContentDescription() { |
|
813 String[] values = headers.getHeader("Content-Description"); |
|
814 if (values != null && values.length > 0) |
|
815 return values[0]; |
|
816 return null; |
|
817 } |
|
818 |
|
819 public void setContentDescription(String description) { |
|
820 headers.setHeader("Content-Description", description); |
|
821 needsSave(); |
|
822 } |
|
823 |
|
824 public abstract SOAPPart getSOAPPart(); |
|
825 |
|
826 public void removeAllAttachments() { |
|
827 try { |
|
828 initializeAllAttachments(); |
|
829 } catch (Exception e) { |
|
830 throw new RuntimeException(e); |
|
831 } |
|
832 |
|
833 if (attachments != null) { |
|
834 attachments.clear(); |
|
835 needsSave(); |
|
836 } |
|
837 } |
|
838 |
|
839 public int countAttachments() { |
|
840 try { |
|
841 initializeAllAttachments(); |
|
842 } catch (Exception e) { |
|
843 throw new RuntimeException(e); |
|
844 } |
|
845 if (attachments != null) |
|
846 return attachments.size(); |
|
847 return 0; |
|
848 } |
|
849 |
|
850 public void addAttachmentPart(AttachmentPart attachment) { |
|
851 try { |
|
852 initializeAllAttachments(); |
|
853 this.optimizeAttachmentProcessing = true; |
|
854 } catch (Exception e) { |
|
855 throw new RuntimeException(e); |
|
856 } |
|
857 if (attachments == null) |
|
858 attachments = new FinalArrayList(); |
|
859 |
|
860 attachments.add(attachment); |
|
861 |
|
862 needsSave(); |
|
863 } |
|
864 |
|
865 static private final Iterator nullIter = Collections.EMPTY_LIST.iterator(); |
|
866 |
|
867 public Iterator getAttachments() { |
|
868 try { |
|
869 initializeAllAttachments(); |
|
870 } catch (Exception e) { |
|
871 throw new RuntimeException(e); |
|
872 } |
|
873 if (attachments == null) |
|
874 return nullIter; |
|
875 return attachments.iterator(); |
|
876 } |
|
877 |
|
878 private void setFinalContentType(String charset) { |
|
879 ContentType ct = contentType(); |
|
880 if (ct == null) { |
|
881 ct = new ContentType(); |
|
882 } |
|
883 String[] split = getExpectedContentType().split("/"); |
|
884 ct.setPrimaryType(split[0]); |
|
885 ct.setSubType(split[1]); |
|
886 ct.setParameter("charset", charset); |
|
887 headers.setHeader("Content-Type", ct.toString()); |
|
888 } |
|
889 |
|
890 private class MimeMatchingIterator implements Iterator { |
|
891 public MimeMatchingIterator(MimeHeaders headers) { |
|
892 this.headers = headers; |
|
893 this.iter = attachments.iterator(); |
|
894 } |
|
895 |
|
896 private Iterator iter; |
|
897 private MimeHeaders headers; |
|
898 private Object nextAttachment; |
|
899 |
|
900 public boolean hasNext() { |
|
901 if (nextAttachment == null) |
|
902 nextAttachment = nextMatch(); |
|
903 return nextAttachment != null; |
|
904 } |
|
905 |
|
906 public Object next() { |
|
907 if (nextAttachment != null) { |
|
908 Object ret = nextAttachment; |
|
909 nextAttachment = null; |
|
910 return ret; |
|
911 } |
|
912 |
|
913 if (hasNext()) |
|
914 return nextAttachment; |
|
915 |
|
916 return null; |
|
917 } |
|
918 |
|
919 Object nextMatch() { |
|
920 while (iter.hasNext()) { |
|
921 AttachmentPartImpl ap = (AttachmentPartImpl) iter.next(); |
|
922 if (ap.hasAllHeaders(headers)) |
|
923 return ap; |
|
924 } |
|
925 return null; |
|
926 } |
|
927 |
|
928 public void remove() { |
|
929 iter.remove(); |
|
930 } |
|
931 } |
|
932 |
|
933 public Iterator getAttachments(MimeHeaders headers) { |
|
934 try { |
|
935 initializeAllAttachments(); |
|
936 } catch (Exception e) { |
|
937 throw new RuntimeException(e); |
|
938 } |
|
939 if (attachments == null) |
|
940 return nullIter; |
|
941 |
|
942 return new MimeMatchingIterator(headers); |
|
943 } |
|
944 |
|
945 public void removeAttachments(MimeHeaders headers) { |
|
946 try { |
|
947 initializeAllAttachments(); |
|
948 } catch (Exception e) { |
|
949 throw new RuntimeException(e); |
|
950 } |
|
951 if (attachments == null) |
|
952 return ; |
|
953 |
|
954 Iterator it = new MimeMatchingIterator(headers); |
|
955 while (it.hasNext()) { |
|
956 int index = attachments.indexOf(it.next()); |
|
957 attachments.set(index, null); |
|
958 } |
|
959 FinalArrayList f = new FinalArrayList(); |
|
960 for (int i = 0; i < attachments.size(); i++) { |
|
961 if (attachments.get(i) != null) { |
|
962 f.add(attachments.get(i)); |
|
963 } |
|
964 } |
|
965 attachments = f; |
|
966 // needsSave(); |
|
967 } |
|
968 |
|
969 public AttachmentPart createAttachmentPart() { |
|
970 return new AttachmentPartImpl(); |
|
971 } |
|
972 |
|
973 public AttachmentPart getAttachment(SOAPElement element) |
|
974 throws SOAPException { |
|
975 try { |
|
976 initializeAllAttachments(); |
|
977 } catch (Exception e) { |
|
978 throw new RuntimeException(e); |
|
979 } |
|
980 String uri; |
|
981 String hrefAttr = element.getAttribute("href"); |
|
982 if ("".equals(hrefAttr)) { |
|
983 Node node = getValueNodeStrict(element); |
|
984 String swaRef = null; |
|
985 if (node != null) { |
|
986 swaRef = node.getValue(); |
|
987 } |
|
988 if (swaRef == null || "".equals(swaRef)) { |
|
989 return null; |
|
990 } else { |
|
991 uri = swaRef; |
|
992 } |
|
993 } else { |
|
994 uri = hrefAttr; |
|
995 } |
|
996 return getAttachmentPart(uri); |
|
997 } |
|
998 |
|
999 private Node getValueNodeStrict(SOAPElement element) { |
|
1000 Node node = (Node)element.getFirstChild(); |
|
1001 if (node != null) { |
|
1002 if (node.getNextSibling() == null |
|
1003 && node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) { |
|
1004 return node; |
|
1005 } else { |
|
1006 return null; |
|
1007 } |
|
1008 } |
|
1009 return null; |
|
1010 } |
|
1011 |
|
1012 |
|
1013 private AttachmentPart getAttachmentPart(String uri) throws SOAPException { |
|
1014 AttachmentPart _part; |
|
1015 try { |
|
1016 if (uri.startsWith("cid:")) { |
|
1017 // rfc2392 |
|
1018 uri = '<'+uri.substring("cid:".length())+'>'; |
|
1019 |
|
1020 MimeHeaders headersToMatch = new MimeHeaders(); |
|
1021 headersToMatch.addHeader(CONTENT_ID, uri); |
|
1022 |
|
1023 Iterator i = this.getAttachments(headersToMatch); |
|
1024 _part = (i == null) ? null : (AttachmentPart)i.next(); |
|
1025 } else { |
|
1026 // try content-location |
|
1027 MimeHeaders headersToMatch = new MimeHeaders(); |
|
1028 headersToMatch.addHeader(CONTENT_LOCATION, uri); |
|
1029 |
|
1030 Iterator i = this.getAttachments(headersToMatch); |
|
1031 _part = (i == null) ? null : (AttachmentPart)i.next(); |
|
1032 } |
|
1033 |
|
1034 // try auto-generated JAXRPC CID |
|
1035 if (_part == null) { |
|
1036 Iterator j = this.getAttachments(); |
|
1037 |
|
1038 while (j.hasNext()) { |
|
1039 AttachmentPart p = (AttachmentPart)j.next(); |
|
1040 String cl = p.getContentId(); |
|
1041 if (cl != null) { |
|
1042 // obtain the partname |
|
1043 int eqIndex = cl.indexOf("="); |
|
1044 if (eqIndex > -1) { |
|
1045 cl = cl.substring(1, eqIndex); |
|
1046 if (cl.equalsIgnoreCase(uri)) { |
|
1047 _part = p; |
|
1048 break; |
|
1049 } |
|
1050 } |
|
1051 } |
|
1052 } |
|
1053 } |
|
1054 |
|
1055 } catch (Exception se) { |
|
1056 log.log(Level.SEVERE, "SAAJ0590.soap.unable.to.locate.attachment", new Object[] {uri}); |
|
1057 throw new SOAPExceptionImpl(se); |
|
1058 } |
|
1059 return _part; |
|
1060 } |
|
1061 |
|
1062 private final InputStream getHeaderBytes() |
|
1063 throws IOException { |
|
1064 SOAPPartImpl sp = (SOAPPartImpl) getSOAPPart(); |
|
1065 return sp.getContentAsStream(); |
|
1066 } |
|
1067 |
|
1068 private String convertToSingleLine(String contentType) { |
|
1069 StringBuffer buffer = new StringBuffer(); |
|
1070 for (int i = 0; i < contentType.length(); i ++) { |
|
1071 char c = contentType.charAt(i); |
|
1072 if (c != '\r' && c != '\n' && c != '\t') |
|
1073 buffer.append(c); |
|
1074 } |
|
1075 return buffer.toString(); |
|
1076 } |
|
1077 |
|
1078 private MimeMultipart getMimeMessage() throws SOAPException { |
|
1079 try { |
|
1080 SOAPPartImpl soapPart = (SOAPPartImpl) getSOAPPart(); |
|
1081 MimeBodyPart mimeSoapPart = soapPart.getMimePart(); |
|
1082 |
|
1083 /* |
|
1084 * Get content type from this message instead of soapPart |
|
1085 * to ensure agreement if soapPart is transcoded (XML <-> FI) |
|
1086 */ |
|
1087 ContentType soapPartCtype = new ContentType(getExpectedContentType()); |
|
1088 |
|
1089 if (!isFastInfoset) { |
|
1090 soapPartCtype.setParameter("charset", initCharset()); |
|
1091 } |
|
1092 mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString()); |
|
1093 |
|
1094 MimeMultipart headerAndBody = null; |
|
1095 |
|
1096 if (!switchOffBM && !switchOffLazyAttachment && |
|
1097 (multiPart != null) && !attachmentsInitialized) { |
|
1098 headerAndBody = new BMMimeMultipart(); |
|
1099 headerAndBody.addBodyPart(mimeSoapPart); |
|
1100 if (attachments != null) { |
|
1101 for (Iterator eachAttachment = attachments.iterator(); |
|
1102 eachAttachment.hasNext();) { |
|
1103 headerAndBody.addBodyPart( |
|
1104 ((AttachmentPartImpl) eachAttachment.next()) |
|
1105 .getMimePart()); |
|
1106 } |
|
1107 } |
|
1108 InputStream in = ((BMMimeMultipart)multiPart).getInputStream(); |
|
1109 if (!((BMMimeMultipart)multiPart).lastBodyPartFound() && |
|
1110 !((BMMimeMultipart)multiPart).isEndOfStream()) { |
|
1111 ((BMMimeMultipart)headerAndBody).setInputStream(in); |
|
1112 ((BMMimeMultipart)headerAndBody).setBoundary( |
|
1113 ((BMMimeMultipart)multiPart).getBoundary()); |
|
1114 ((BMMimeMultipart)headerAndBody). |
|
1115 setLazyAttachments(lazyAttachments); |
|
1116 } |
|
1117 |
|
1118 } else { |
|
1119 headerAndBody = new MimeMultipart(); |
|
1120 headerAndBody.addBodyPart(mimeSoapPart); |
|
1121 |
|
1122 for (Iterator eachAttachement = getAttachments(); |
|
1123 eachAttachement.hasNext(); |
|
1124 ) { |
|
1125 headerAndBody.addBodyPart( |
|
1126 ((AttachmentPartImpl) eachAttachement.next()) |
|
1127 .getMimePart()); |
|
1128 } |
|
1129 } |
|
1130 |
|
1131 ContentType contentType = headerAndBody.getContentType(); |
|
1132 |
|
1133 ParameterList l = contentType.getParameterList(); |
|
1134 |
|
1135 // set content type depending on SOAP version |
|
1136 l.set("type", getExpectedContentType()); |
|
1137 l.set("boundary", contentType.getParameter("boundary")); |
|
1138 ContentType nct = new ContentType("multipart", "related", l); |
|
1139 |
|
1140 headers.setHeader( |
|
1141 "Content-Type", |
|
1142 convertToSingleLine(nct.toString())); |
|
1143 // TBD |
|
1144 // Set content length MIME header here. |
|
1145 |
|
1146 return headerAndBody; |
|
1147 } catch (SOAPException ex) { |
|
1148 throw ex; |
|
1149 } catch (Throwable ex) { |
|
1150 log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj"); |
|
1151 throw new SOAPExceptionImpl( |
|
1152 "Unable to convert SOAP message into " |
|
1153 + "a MimeMultipart object", |
|
1154 ex); |
|
1155 } |
|
1156 } |
|
1157 |
|
1158 private String initCharset() { |
|
1159 |
|
1160 String charset = null; |
|
1161 |
|
1162 String[] cts = getMimeHeaders().getHeader("Content-Type"); |
|
1163 if ((cts != null) && (cts[0] != null)) { |
|
1164 charset = getCharsetString(cts[0]); |
|
1165 } |
|
1166 |
|
1167 if (charset == null) { |
|
1168 charset = (String) getProperty(CHARACTER_SET_ENCODING); |
|
1169 } |
|
1170 |
|
1171 if (charset != null) { |
|
1172 return charset; |
|
1173 } |
|
1174 |
|
1175 return "utf-8"; |
|
1176 } |
|
1177 |
|
1178 private String getCharsetString(String s) { |
|
1179 try { |
|
1180 int index = s.indexOf(";"); |
|
1181 if(index < 0) |
|
1182 return null; |
|
1183 ParameterList pl = new ParameterList(s.substring(index)); |
|
1184 return pl.get("charset"); |
|
1185 } catch(Exception e) { |
|
1186 return null; |
|
1187 } |
|
1188 } |
|
1189 |
|
1190 public void saveChanges() throws SOAPException { |
|
1191 |
|
1192 // suck in all the data from the attachments and have it |
|
1193 // ready for writing/sending etc. |
|
1194 |
|
1195 String charset = initCharset(); |
|
1196 |
|
1197 /*if (countAttachments() == 0) {*/ |
|
1198 int attachmentCount = (attachments == null) ? 0 : attachments.size(); |
|
1199 if (attachmentCount == 0) { |
|
1200 if (!switchOffBM && !switchOffLazyAttachment && |
|
1201 !attachmentsInitialized && (multiPart != null)) { |
|
1202 // so there might be attachments |
|
1203 attachmentCount = 1; |
|
1204 } |
|
1205 } |
|
1206 |
|
1207 try { |
|
1208 if ((attachmentCount == 0) && !hasXOPContent()) { |
|
1209 InputStream in; |
|
1210 try{ |
|
1211 /* |
|
1212 * Not sure why this is called getHeaderBytes(), but it actually |
|
1213 * returns the whole message as a byte stream. This stream could |
|
1214 * be either XML of Fast depending on the mode. |
|
1215 */ |
|
1216 in = getHeaderBytes(); |
|
1217 // no attachments, hence this property can be false |
|
1218 this.optimizeAttachmentProcessing = false; |
|
1219 if (SOAPPartImpl.lazyContentLength) { |
|
1220 inputStreamAfterSaveChanges = in; |
|
1221 } |
|
1222 } catch (IOException ex) { |
|
1223 log.severe("SAAJ0539.soap.cannot.get.header.stream"); |
|
1224 throw new SOAPExceptionImpl( |
|
1225 "Unable to get header stream in saveChanges: ", |
|
1226 ex); |
|
1227 } |
|
1228 |
|
1229 if (in instanceof ByteInputStream) { |
|
1230 ByteInputStream bIn = (ByteInputStream)in; |
|
1231 messageBytes = bIn.getBytes(); |
|
1232 messageByteCount = bIn.getCount(); |
|
1233 } |
|
1234 |
|
1235 setFinalContentType(charset); |
|
1236 /* |
|
1237 headers.setHeader( |
|
1238 "Content-Type", |
|
1239 getExpectedContentType() + |
|
1240 (isFastInfoset ? "" : "; charset=" + charset));*/ |
|
1241 if (messageByteCount > 0) { |
|
1242 headers.setHeader( |
|
1243 "Content-Length", |
|
1244 Integer.toString(messageByteCount)); |
|
1245 } |
|
1246 } else { |
|
1247 if(hasXOPContent()) |
|
1248 mmp = getXOPMessage(); |
|
1249 else |
|
1250 mmp = getMimeMessage(); |
|
1251 } |
|
1252 } catch (Throwable ex) { |
|
1253 log.severe("SAAJ0540.soap.err.saving.multipart.msg"); |
|
1254 throw new SOAPExceptionImpl( |
|
1255 "Error during saving a multipart message", |
|
1256 ex); |
|
1257 } |
|
1258 |
|
1259 // FIX ME -- SOAP Action replaced by Content-Type optional parameter action |
|
1260 /* |
|
1261 if(isCorrectSoapVersion(SOAP1_1_FLAG)) { |
|
1262 |
|
1263 String[] soapAction = headers.getHeader("SOAPAction"); |
|
1264 |
|
1265 if (soapAction == null || soapAction.length == 0) |
|
1266 headers.setHeader("SOAPAction", "\"\""); |
|
1267 |
|
1268 } |
|
1269 */ |
|
1270 |
|
1271 saved = true; |
|
1272 } |
|
1273 |
|
1274 private MimeMultipart getXOPMessage() throws SOAPException { |
|
1275 try { |
|
1276 MimeMultipart headerAndBody = new MimeMultipart(); |
|
1277 SOAPPartImpl soapPart = (SOAPPartImpl)getSOAPPart(); |
|
1278 MimeBodyPart mimeSoapPart = soapPart.getMimePart(); |
|
1279 ContentType soapPartCtype = |
|
1280 new ContentType("application/xop+xml"); |
|
1281 soapPartCtype.setParameter("type", getExpectedContentType()); |
|
1282 String charset = initCharset(); |
|
1283 soapPartCtype.setParameter("charset", charset); |
|
1284 mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString()); |
|
1285 headerAndBody.addBodyPart(mimeSoapPart); |
|
1286 |
|
1287 for (Iterator eachAttachement = getAttachments(); |
|
1288 eachAttachement.hasNext(); |
|
1289 ) { |
|
1290 headerAndBody.addBodyPart( |
|
1291 ((AttachmentPartImpl) eachAttachement.next()) |
|
1292 .getMimePart()); |
|
1293 } |
|
1294 |
|
1295 ContentType contentType = headerAndBody.getContentType(); |
|
1296 |
|
1297 ParameterList l = contentType.getParameterList(); |
|
1298 |
|
1299 //lets not write start-info for now till we get servlet fix done |
|
1300 l.set("start-info", getExpectedContentType());//+";charset="+initCharset()); |
|
1301 |
|
1302 // set content type depending on SOAP version |
|
1303 l.set("type", "application/xop+xml"); |
|
1304 |
|
1305 if (isCorrectSoapVersion(SOAP1_2_FLAG)) { |
|
1306 String action = getAction(); |
|
1307 if(action != null) |
|
1308 l.set("action", action); |
|
1309 } |
|
1310 |
|
1311 l.set("boundary", contentType.getParameter("boundary")); |
|
1312 ContentType nct = new ContentType("Multipart", "Related", l); |
|
1313 headers.setHeader( |
|
1314 "Content-Type", |
|
1315 convertToSingleLine(nct.toString())); |
|
1316 // TBD |
|
1317 // Set content length MIME header here. |
|
1318 |
|
1319 return headerAndBody; |
|
1320 } catch (SOAPException ex) { |
|
1321 throw ex; |
|
1322 } catch (Throwable ex) { |
|
1323 log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj"); |
|
1324 throw new SOAPExceptionImpl( |
|
1325 "Unable to convert SOAP message into " |
|
1326 + "a MimeMultipart object", |
|
1327 ex); |
|
1328 } |
|
1329 |
|
1330 } |
|
1331 |
|
1332 private boolean hasXOPContent() throws ParseException { |
|
1333 String type = getContentType(); |
|
1334 if(type == null) |
|
1335 return false; |
|
1336 ContentType ct = new ContentType(type); |
|
1337 //return isMimeMultipartXOPPackage(ct) || isSOAPBodyXOPPackage(ct); |
|
1338 return isMimeMultipartXOPSoap1_1Package(ct) || |
|
1339 isMimeMultipartXOPSoap1_2Package(ct) || isSOAPBodyXOPPackage(ct); |
|
1340 |
|
1341 } |
|
1342 |
|
1343 public void writeTo(OutputStream out) throws SOAPException, IOException { |
|
1344 if (saveRequired()){ |
|
1345 this.optimizeAttachmentProcessing = true; |
|
1346 saveChanges(); |
|
1347 } |
|
1348 |
|
1349 if(!optimizeAttachmentProcessing){ |
|
1350 if (SOAPPartImpl.lazyContentLength && messageByteCount <= 0) { |
|
1351 byte[] buf = new byte[1024]; |
|
1352 |
|
1353 int length = 0; |
|
1354 while( (length = inputStreamAfterSaveChanges.read(buf)) != -1) { |
|
1355 out.write(buf,0, length); |
|
1356 messageByteCount += length; |
|
1357 } |
|
1358 if (messageByteCount > 0) { |
|
1359 headers.setHeader( |
|
1360 "Content-Length", |
|
1361 Integer.toString(messageByteCount)); |
|
1362 } |
|
1363 } else { |
|
1364 out.write(messageBytes, 0, messageByteCount); |
|
1365 } |
|
1366 } |
|
1367 else{ |
|
1368 try{ |
|
1369 if(hasXOPContent()){ |
|
1370 mmp.writeTo(out); |
|
1371 }else{ |
|
1372 mmp.writeTo(out); |
|
1373 if (!switchOffBM && !switchOffLazyAttachment && |
|
1374 (multiPart != null) && !attachmentsInitialized) { |
|
1375 ((BMMimeMultipart)multiPart).setInputStream( |
|
1376 ((BMMimeMultipart)mmp).getInputStream()); |
|
1377 } |
|
1378 } |
|
1379 } catch(Exception ex){ |
|
1380 log.severe("SAAJ0540.soap.err.saving.multipart.msg"); |
|
1381 throw new SOAPExceptionImpl( |
|
1382 "Error during saving a multipart message", |
|
1383 ex); |
|
1384 } |
|
1385 } |
|
1386 |
|
1387 if(isCorrectSoapVersion(SOAP1_1_FLAG)) { |
|
1388 |
|
1389 String[] soapAction = headers.getHeader("SOAPAction"); |
|
1390 |
|
1391 if (soapAction == null || soapAction.length == 0) |
|
1392 headers.setHeader("SOAPAction", "\"\""); |
|
1393 |
|
1394 } |
|
1395 |
|
1396 messageBytes = null; |
|
1397 needsSave(); |
|
1398 } |
|
1399 |
|
1400 public SOAPBody getSOAPBody() throws SOAPException { |
|
1401 SOAPBody body = getSOAPPart().getEnvelope().getBody(); |
|
1402 /*if (body == null) { |
|
1403 throw new SOAPException("No SOAP Body was found in the SOAP Message"); |
|
1404 }*/ |
|
1405 return body; |
|
1406 } |
|
1407 |
|
1408 public SOAPHeader getSOAPHeader() throws SOAPException { |
|
1409 SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader(); |
|
1410 /*if (hdr == null) { |
|
1411 throw new SOAPException("No SOAP Header was found in the SOAP Message"); |
|
1412 }*/ |
|
1413 return hdr; |
|
1414 } |
|
1415 |
|
1416 private void initializeAllAttachments () |
|
1417 throws MessagingException, SOAPException { |
|
1418 if (switchOffBM || switchOffLazyAttachment) { |
|
1419 return; |
|
1420 } |
|
1421 |
|
1422 if (attachmentsInitialized || (multiPart == null)) { |
|
1423 return; |
|
1424 } |
|
1425 |
|
1426 if (attachments == null) |
|
1427 attachments = new FinalArrayList(); |
|
1428 |
|
1429 int count = multiPart.getCount(); |
|
1430 for (int i=0; i < count; i++ ) { |
|
1431 initializeAttachment(multiPart.getBodyPart(i)); |
|
1432 } |
|
1433 attachmentsInitialized = true; |
|
1434 //multiPart = null; |
|
1435 needsSave(); |
|
1436 } |
|
1437 |
|
1438 private void initializeAttachment(MimeBodyPart mbp) throws SOAPException { |
|
1439 AttachmentPartImpl attachmentPart = new AttachmentPartImpl(); |
|
1440 DataHandler attachmentHandler = mbp.getDataHandler(); |
|
1441 attachmentPart.setDataHandler(attachmentHandler); |
|
1442 |
|
1443 AttachmentPartImpl.copyMimeHeaders(mbp, attachmentPart); |
|
1444 attachments.add(attachmentPart); |
|
1445 } |
|
1446 |
|
1447 private void initializeAttachment(MimeMultipart multiPart, int i) |
|
1448 throws Exception { |
|
1449 MimeBodyPart currentBodyPart = multiPart.getBodyPart(i); |
|
1450 AttachmentPartImpl attachmentPart = new AttachmentPartImpl(); |
|
1451 |
|
1452 DataHandler attachmentHandler = currentBodyPart.getDataHandler(); |
|
1453 attachmentPart.setDataHandler(attachmentHandler); |
|
1454 |
|
1455 AttachmentPartImpl.copyMimeHeaders(currentBodyPart, attachmentPart); |
|
1456 addAttachmentPart(attachmentPart); |
|
1457 } |
|
1458 |
|
1459 private void setMimeHeaders(SOAPPart soapPart, |
|
1460 MimeBodyPart soapMessagePart) throws Exception { |
|
1461 |
|
1462 // first remove the existing content-type |
|
1463 soapPart.removeAllMimeHeaders(); |
|
1464 // add everything present in soapMessagePart |
|
1465 List headers = soapMessagePart.getAllHeaders(); |
|
1466 int sz = headers.size(); |
|
1467 for( int i=0; i<sz; i++ ) { |
|
1468 Header h = (Header) headers.get(i); |
|
1469 soapPart.addMimeHeader(h.getName(), h.getValue()); |
|
1470 } |
|
1471 } |
|
1472 |
|
1473 private void initCharsetProperty(ContentType contentType) { |
|
1474 String charset = contentType.getParameter("charset"); |
|
1475 if (charset != null) { |
|
1476 ((SOAPPartImpl) getSOAPPart()).setSourceCharsetEncoding(charset); |
|
1477 if(!charset.equalsIgnoreCase("utf-8")) |
|
1478 setProperty(CHARACTER_SET_ENCODING, charset); |
|
1479 } |
|
1480 } |
|
1481 |
|
1482 public void setLazyAttachments(boolean flag) { |
|
1483 lazyAttachments = flag; |
|
1484 } |
|
1485 |
|
1486 } |
|