1 /*
2 * Copyright (c) 1997, 2013, 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
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<AttachmentPart> 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;
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);
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 }
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) {
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",
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) {
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<AttachmentPart>();
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<AttachmentPart> {
891 public MimeMatchingIterator(MimeHeaders headers) {
892 this.headers = headers;
893 this.iter = attachments.iterator();
894 }
895
896 private Iterator<AttachmentPart> iter;
897 private MimeHeaders headers;
898 private AttachmentPart nextAttachment;
899
900 public boolean hasNext() {
901 if (nextAttachment == null)
902 nextAttachment = nextMatch();
903 return nextAttachment != null;
904 }
905
906 public AttachmentPart next() {
907 if (nextAttachment != null) {
908 AttachmentPart ret = nextAttachment;
909 nextAttachment = null;
910 return ret;
911 }
912
913 if (hasNext())
914 return nextAttachment;
915
916 return null;
917 }
918
919 AttachmentPart 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<AttachmentPart> it = new MimeMatchingIterator(headers);
955 while (it.hasNext()) {
956 int index = attachments.indexOf(it.next());
957 attachments.set(index, null);
958 }
959 FinalArrayList<AttachmentPart> f = new FinalArrayList<AttachmentPart>();
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 }
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;
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 }
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<AttachmentPart>();
|
1 /*
2 * Copyright (c) 1997, 2017, 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
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<AttachmentPart> 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;
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 * @param isFastInfoset whether it is fast infoset
207 * @param acceptFastInfoset whether to accept fast infoset
208 */
209 protected MessageImpl(boolean isFastInfoset, boolean acceptFastInfoset) {
210 this.isFastInfoset = isFastInfoset;
211 this.acceptFastInfoset = acceptFastInfoset;
212
213 headers = new MimeHeaders();
214 headers.setHeader("Accept", getExpectedAcceptHeader());
215 contentType = new ContentType();
216 }
217
218 /**
219 * Shallow copy.
220 *
221 * @param msg SoapMessage
222 */
223 protected MessageImpl(SOAPMessage msg) {
224 if (!(msg instanceof MessageImpl)) {
225 // don't know how to handle this.
226 }
227 MessageImpl src = (MessageImpl) msg;
228 this.headers = src.headers;
229 this.soapPartImpl = src.soapPartImpl;
230 this.attachments = src.attachments;
231 this.saved = src.saved;
232 this.messageBytes = src.messageBytes;
233 this.messageByteCount = src.messageByteCount;
234 this.properties = src.properties;
235 this.contentType = src.contentType;
236 }
237
238 /**
239 * @param stat
240 * the mask value obtained from {@link #identifyContentType(ContentType)}
241 * @return true if SOAP 1.1 Content
242 */
243 protected static boolean isSoap1_1Content(int stat) {
244 return (stat & SOAP1_1_FLAG) != 0;
245 }
246
247 /**
248 * Check whether it is SOAP 1.2 content.
249 * @param stat
250 * the mask value obtained from {@link #identifyContentType(ContentType)}
251 * @return true if it is SOAP 1.2 content
252 */
253 protected static boolean isSoap1_2Content(int stat) {
254 return (stat & SOAP1_2_FLAG) != 0;
255 }
256
257 private static boolean isMimeMultipartXOPSoap1_2Package(ContentType contentType) {
258 String type = contentType.getParameter("type");
259 if (type == null) {
260 return false;
261 }
262 type = type.toLowerCase();
263 if (!type.startsWith("application/xop+xml")) {
264 return false;
265 }
266 String startinfo = contentType.getParameter("start-info");
267 if (startinfo == null) {
268 return false;
269 }
270 startinfo = startinfo.toLowerCase();
271 return isEqualToSoap1_2Type(startinfo);
289 return isEqualToSoap1_1Type(startinfo);
290 }
291
292 private static boolean isSOAPBodyXOPPackage(ContentType contentType){
293 String primary = contentType.getPrimaryType();
294 String sub = contentType.getSubType();
295
296 if (primary.equalsIgnoreCase("application")) {
297 if (sub.equalsIgnoreCase("xop+xml")) {
298 String type = getTypeParameter(contentType);
299 return isEqualToSoap1_2Type(type) || isEqualToSoap1_1Type(type);
300 }
301 }
302 return false;
303 }
304
305 /**
306 * Construct a message from an input stream. When messages are
307 * received, there's two parts -- the transport headers and the
308 * message content in a transport specific stream.
309 * @param headers MimeHeaders
310 * @param in InputStream
311 * @exception SOAPExceptionImpl in case of I/O error
312 */
313 protected MessageImpl(MimeHeaders headers, final InputStream in)
314 throws SOAPExceptionImpl {
315 contentType = parseContentType(headers);
316 init(headers,identifyContentType(contentType),contentType,in);
317 }
318
319 private static ContentType parseContentType(MimeHeaders headers) throws SOAPExceptionImpl {
320 final String ct;
321 if (headers != null)
322 ct = getContentType(headers);
323 else {
324 log.severe("SAAJ0550.soap.null.headers");
325 throw new SOAPExceptionImpl("Cannot create message: " +
326 "Headers can't be null");
327 }
328
329 if (ct == null) {
330 log.severe("SAAJ0532.soap.no.Content-Type");
331 throw new SOAPExceptionImpl("Absent Content-Type");
332 }
333 try {
334 return new ContentType(ct);
335 } catch (Throwable ex) {
336 log.severe("SAAJ0535.soap.cannot.internalize.message");
337 throw new SOAPExceptionImpl("Unable to internalize message", ex);
338 }
339 }
340
341 /**
342 * Construct a message from an input stream. When messages are
343 * received, there's two parts -- the transport headers and the
344 * message content in a transport specific stream.
345 *
346 * @param headers headers
347 * @param contentType
348 * The parsed content type header from the headers variable.
349 * This is redundant parameter, but it avoids reparsing this header again.
350 * @param stat
351 * The result of {@link #identifyContentType(ContentType)} over
352 * the contentType parameter. This redundant parameter, but it avoids
353 * recomputing this information again.
354 * @param in input stream
355 * @exception SOAPExceptionImpl in case of an error
356 */
357 protected MessageImpl(MimeHeaders headers, final ContentType contentType, int stat, final InputStream in) throws SOAPExceptionImpl {
358 init(headers, stat, contentType, in);
359
360 }
361
362 public MessageImpl(MimeHeaders headers, ContentType ct, int stat,
363 XMLStreamReader reader) throws SOAPExceptionImpl {
364 init(headers, stat, ct, reader);
365 }
366
367 private void init(MimeHeaders headers, int stat, final ContentType contentType, final Object input) throws SOAPExceptionImpl {
368 this.headers = headers;
369
370 try {
371
372 // Set isFastInfoset/acceptFastInfoset flag based on MIME type
373 if ((stat & FI_ENCODED_FLAG) > 0) {
374 isFastInfoset = acceptFastInfoset = true;
375 }
422 initCharsetProperty(contentType);
423 getSOAPPart().setContent(new StreamSource(in));
424 }
425 } else {
426 //is a StAX reader
427 if (isFastInfoset) {
428 //need to get FI stax reader
429 } else {
430 initCharsetProperty(contentType);
431 getSOAPPart().setContent(new StAXSource(rdr));
432 }
433 }
434 }
435 else if ((stat & MIME_MULTIPART_FLAG) != 0 && in == null) {
436 //only parse multipart in the inputstream case
437 //in stax reader case, we would be given the attachments separately
438 getSOAPPart().setContent(new StAXSource(rdr));
439 } else if ((stat & MIME_MULTIPART_FLAG) != 0) {
440 final InputStream finalIn = in;
441 DataSource ds = new DataSource() {
442 @Override
443 public InputStream getInputStream() {
444 return finalIn;
445 }
446
447 @Override
448 public OutputStream getOutputStream() {
449 return null;
450 }
451
452 @Override
453 public String getContentType() {
454 return contentType.toString();
455 }
456
457 @Override
458 public String getName() {
459 return "";
460 }
461 };
462
463 multiPart = null;
464 if (useMimePull) {
465 multiPart = new MimePullMultipart(ds,contentType);
466 } else if (switchOffBM) {
467 multiPart = new MimeMultipart(ds,contentType);
468 } else {
469 multiPart = new BMMimeMultipart(ds,contentType);
470 }
471
472 String startParam = contentType.getParameter("start");
473 MimeBodyPart soapMessagePart = null;
474 InputStream soapPartInputStream = null;
475 String contentID = null;
476 String contentIDNoAngle = null;
477 if (switchOffBM || switchOffLazyAttachment) {
592
593 public void setIsFastInfoset(boolean value) {
594 if (value != isFastInfoset) {
595 isFastInfoset = value;
596 if (isFastInfoset) {
597 acceptFastInfoset = true;
598 }
599 saved = false; // ensure transcoding if necessary
600 }
601 }
602
603 public boolean isLazySoapBodyParsing() {
604 Object lazyParsingProp = getProperty(LAZY_SOAP_BODY_PARSING);
605 if (lazyParsingProp == null) return false;
606 if (lazyParsingProp instanceof Boolean) {
607 return ((Boolean) lazyParsingProp).booleanValue();
608 } else {
609 return Boolean.valueOf(lazyParsingProp.toString());
610 }
611 }
612 @Override
613 public Object getProperty(String property) {
614 return properties.get(property);
615 }
616
617 @Override
618 public void setProperty(String property, Object value) {
619 verify(property, value);
620 properties.put(property, value);
621 }
622
623 private void verify(String property, Object value) {
624 if (property.equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) {
625 if (!("true".equals(value) || "false".equals(value)))
626 throw new RuntimeException(
627 property + " must have value false or true");
628
629 try {
630 EnvelopeImpl env = (EnvelopeImpl) getSOAPPart().getEnvelope();
631 if ("true".equalsIgnoreCase((String)value)) {
632 env.setOmitXmlDecl("no");
633 } else if ("false".equalsIgnoreCase((String)value)) {
634 env.setOmitXmlDecl("yes");
635 }
636 } catch (Exception e) {
637 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property",
725 throw new SOAPExceptionImpl(
726 "Invalid Content-Type:"
727 + primary
728 + '/'
729 + sub
730 + ". Is this an error message instead of a SOAP response?");
731 }
732 }
733
734 /**
735 * Obtains the type parameter of the Content-Type header. Defaults to "text/xml".
736 */
737 private static String getTypeParameter(ContentType contentType) {
738 String p = contentType.getParameter("type");
739 if(p!=null)
740 return p.toLowerCase();
741 else
742 return "text/xml";
743 }
744
745 @Override
746 public MimeHeaders getMimeHeaders() {
747 return this.headers;
748 }
749
750 final static String getContentType(MimeHeaders headers) {
751 String[] values = headers.getHeader("Content-Type");
752 if (values == null)
753 return null;
754 else
755 return values[0];
756 }
757
758 /*
759 * Get the complete ContentType value along with optional parameters.
760 */
761 public String getContentType() {
762 return getContentType(this.headers);
763 }
764
765 public void setContentType(String type) {
809 public String getCharset() {
810 return contentType().getParameter("charset");
811 }
812
813 public void setCharset(String charset) {
814 ContentType ct = contentType();
815 ct.setParameter("charset", charset);
816 headers.setHeader("Content-Type", ct.toString());
817 needsSave();
818 }
819
820 /**
821 * All write methods (i.e setters) should call this method in
822 * order to make sure that a save is necessary since the state
823 * has been modified.
824 */
825 private final void needsSave() {
826 saved = false;
827 }
828
829 @Override
830 public boolean saveRequired() {
831 return saved != true;
832 }
833
834 @Override
835 public String getContentDescription() {
836 String[] values = headers.getHeader("Content-Description");
837 if (values != null && values.length > 0)
838 return values[0];
839 return null;
840 }
841
842 @Override
843 public void setContentDescription(String description) {
844 headers.setHeader("Content-Description", description);
845 needsSave();
846 }
847
848 @Override
849 public abstract SOAPPart getSOAPPart();
850
851 @Override
852 public void removeAllAttachments() {
853 try {
854 initializeAllAttachments();
855 } catch (Exception e) {
856 throw new RuntimeException(e);
857 }
858
859 if (attachments != null) {
860 attachments.clear();
861 needsSave();
862 }
863 }
864
865 @Override
866 public int countAttachments() {
867 try {
868 initializeAllAttachments();
869 } catch (Exception e) {
870 throw new RuntimeException(e);
871 }
872 if (attachments != null)
873 return attachments.size();
874 return 0;
875 }
876
877 @Override
878 public void addAttachmentPart(AttachmentPart attachment) {
879 try {
880 initializeAllAttachments();
881 this.optimizeAttachmentProcessing = true;
882 } catch (Exception e) {
883 throw new RuntimeException(e);
884 }
885 if (attachments == null)
886 attachments = new FinalArrayList<AttachmentPart>();
887
888 attachments.add(attachment);
889
890 needsSave();
891 }
892
893 static private final Iterator nullIter = Collections.EMPTY_LIST.iterator();
894
895 @Override
896 public Iterator getAttachments() {
897 try {
898 initializeAllAttachments();
899 } catch (Exception e) {
900 throw new RuntimeException(e);
901 }
902 if (attachments == null)
903 return nullIter;
904 return attachments.iterator();
905 }
906
907 private void setFinalContentType(String charset) {
908 ContentType ct = contentType();
909 if (ct == null) {
910 ct = new ContentType();
911 }
912 String[] split = getExpectedContentType().split("/");
913 ct.setPrimaryType(split[0]);
914 ct.setSubType(split[1]);
915 ct.setParameter("charset", charset);
916 headers.setHeader("Content-Type", ct.toString());
917 }
918
919 private class MimeMatchingIterator implements Iterator<AttachmentPart> {
920 public MimeMatchingIterator(MimeHeaders headers) {
921 this.headers = headers;
922 this.iter = attachments.iterator();
923 }
924
925 private Iterator<AttachmentPart> iter;
926 private MimeHeaders headers;
927 private AttachmentPart nextAttachment;
928
929 @Override
930 public boolean hasNext() {
931 if (nextAttachment == null)
932 nextAttachment = nextMatch();
933 return nextAttachment != null;
934 }
935
936 @Override
937 public AttachmentPart next() {
938 if (nextAttachment != null) {
939 AttachmentPart ret = nextAttachment;
940 nextAttachment = null;
941 return ret;
942 }
943
944 if (hasNext())
945 return nextAttachment;
946
947 return null;
948 }
949
950 AttachmentPart nextMatch() {
951 while (iter.hasNext()) {
952 AttachmentPartImpl ap = (AttachmentPartImpl) iter.next();
953 if (ap.hasAllHeaders(headers))
954 return ap;
955 }
956 return null;
957 }
958
959 @Override
960 public void remove() {
961 iter.remove();
962 }
963 }
964
965 @Override
966 public Iterator getAttachments(MimeHeaders headers) {
967 try {
968 initializeAllAttachments();
969 } catch (Exception e) {
970 throw new RuntimeException(e);
971 }
972 if (attachments == null)
973 return nullIter;
974
975 return new MimeMatchingIterator(headers);
976 }
977
978 @Override
979 public void removeAttachments(MimeHeaders headers) {
980 try {
981 initializeAllAttachments();
982 } catch (Exception e) {
983 throw new RuntimeException(e);
984 }
985 if (attachments == null)
986 return ;
987
988 Iterator<AttachmentPart> it = new MimeMatchingIterator(headers);
989 while (it.hasNext()) {
990 int index = attachments.indexOf(it.next());
991 attachments.set(index, null);
992 }
993 FinalArrayList<AttachmentPart> f = new FinalArrayList<AttachmentPart>();
994 for (int i = 0; i < attachments.size(); i++) {
995 if (attachments.get(i) != null) {
996 f.add(attachments.get(i));
997 }
998 }
999 attachments = f;
1000 // needsSave();
1001 }
1002
1003 @Override
1004 public AttachmentPart createAttachmentPart() {
1005 return new AttachmentPartImpl();
1006 }
1007
1008 @Override
1009 public AttachmentPart getAttachment(SOAPElement element)
1010 throws SOAPException {
1011 try {
1012 initializeAllAttachments();
1013 } catch (Exception e) {
1014 throw new RuntimeException(e);
1015 }
1016 String uri;
1017 String hrefAttr = element.getAttribute("href");
1018 if ("".equals(hrefAttr)) {
1019 Node node = getValueNodeStrict(element);
1020 String swaRef = null;
1021 if (node != null) {
1022 swaRef = node.getValue();
1023 }
1024 if (swaRef == null || "".equals(swaRef)) {
1025 return null;
1026 } else {
1027 uri = swaRef;
1028 }
1206
1207 if (charset != null) {
1208 return charset;
1209 }
1210
1211 return "utf-8";
1212 }
1213
1214 private String getCharsetString(String s) {
1215 try {
1216 int index = s.indexOf(";");
1217 if(index < 0)
1218 return null;
1219 ParameterList pl = new ParameterList(s.substring(index));
1220 return pl.get("charset");
1221 } catch(Exception e) {
1222 return null;
1223 }
1224 }
1225
1226 @Override
1227 public void saveChanges() throws SOAPException {
1228
1229 // suck in all the data from the attachments and have it
1230 // ready for writing/sending etc.
1231
1232 String charset = initCharset();
1233
1234 /*if (countAttachments() == 0) {*/
1235 int attachmentCount = (attachments == null) ? 0 : attachments.size();
1236 if (attachmentCount == 0) {
1237 if (!switchOffBM && !switchOffLazyAttachment &&
1238 !attachmentsInitialized && (multiPart != null)) {
1239 // so there might be attachments
1240 attachmentCount = 1;
1241 }
1242 }
1243
1244 try {
1245 if ((attachmentCount == 0) && !hasXOPContent()) {
1246 InputStream in;
1360 log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
1361 throw new SOAPExceptionImpl(
1362 "Unable to convert SOAP message into "
1363 + "a MimeMultipart object",
1364 ex);
1365 }
1366
1367 }
1368
1369 private boolean hasXOPContent() throws ParseException {
1370 String type = getContentType();
1371 if(type == null)
1372 return false;
1373 ContentType ct = new ContentType(type);
1374 //return isMimeMultipartXOPPackage(ct) || isSOAPBodyXOPPackage(ct);
1375 return isMimeMultipartXOPSoap1_1Package(ct) ||
1376 isMimeMultipartXOPSoap1_2Package(ct) || isSOAPBodyXOPPackage(ct);
1377
1378 }
1379
1380 @Override
1381 public void writeTo(OutputStream out) throws SOAPException, IOException {
1382 if (saveRequired()){
1383 this.optimizeAttachmentProcessing = true;
1384 saveChanges();
1385 }
1386
1387 if(!optimizeAttachmentProcessing){
1388 if (SOAPPartImpl.lazyContentLength && messageByteCount <= 0) {
1389 byte[] buf = new byte[1024];
1390
1391 int length = 0;
1392 while( (length = inputStreamAfterSaveChanges.read(buf)) != -1) {
1393 out.write(buf,0, length);
1394 messageByteCount += length;
1395 }
1396 if (messageByteCount > 0) {
1397 headers.setHeader(
1398 "Content-Length",
1399 Integer.toString(messageByteCount));
1400 }
1418 log.severe("SAAJ0540.soap.err.saving.multipart.msg");
1419 throw new SOAPExceptionImpl(
1420 "Error during saving a multipart message",
1421 ex);
1422 }
1423 }
1424
1425 if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
1426
1427 String[] soapAction = headers.getHeader("SOAPAction");
1428
1429 if (soapAction == null || soapAction.length == 0)
1430 headers.setHeader("SOAPAction", "\"\"");
1431
1432 }
1433
1434 messageBytes = null;
1435 needsSave();
1436 }
1437
1438 @Override
1439 public SOAPBody getSOAPBody() throws SOAPException {
1440 SOAPBody body = getSOAPPart().getEnvelope().getBody();
1441 /*if (body == null) {
1442 throw new SOAPException("No SOAP Body was found in the SOAP Message");
1443 }*/
1444 return body;
1445 }
1446
1447 @Override
1448 public SOAPHeader getSOAPHeader() throws SOAPException {
1449 SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader();
1450 /*if (hdr == null) {
1451 throw new SOAPException("No SOAP Header was found in the SOAP Message");
1452 }*/
1453 return hdr;
1454 }
1455
1456 private void initializeAllAttachments ()
1457 throws MessagingException, SOAPException {
1458 if (switchOffBM || switchOffLazyAttachment) {
1459 return;
1460 }
1461
1462 if (attachmentsInitialized || (multiPart == null)) {
1463 return;
1464 }
1465
1466 if (attachments == null)
1467 attachments = new FinalArrayList<AttachmentPart>();
|