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