Thu, 31 Aug 2017 15:18:52 +0800
merge
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
23 * questions.
24 */
26 package com.sun.xml.internal.ws.encoding.xml;
28 import com.sun.istack.internal.NotNull;
29 import com.sun.xml.internal.bind.api.Bridge;
30 import com.sun.xml.internal.ws.api.SOAPVersion;
31 import com.sun.xml.internal.ws.api.WSFeatureList;
32 import com.sun.xml.internal.ws.api.message.*;
33 import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
34 import com.sun.xml.internal.ws.api.pipe.Codec;
35 import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory;
36 import com.sun.xml.internal.ws.developer.StreamingAttachmentFeature;
37 import com.sun.xml.internal.ws.encoding.ContentType;
38 import com.sun.xml.internal.ws.encoding.MimeMultipartParser;
39 import com.sun.xml.internal.ws.encoding.XMLHTTPBindingCodec;
40 import com.sun.xml.internal.ws.message.AbstractMessageImpl;
41 import com.sun.xml.internal.ws.message.EmptyMessageImpl;
42 import com.sun.xml.internal.ws.message.MimeAttachmentSet;
43 import com.sun.xml.internal.ws.message.source.PayloadSourceMessage;
44 import com.sun.xml.internal.ws.util.ByteArrayBuffer;
45 import com.sun.xml.internal.ws.util.StreamUtils;
46 import org.xml.sax.ContentHandler;
47 import org.xml.sax.ErrorHandler;
48 import org.xml.sax.SAXException;
50 import javax.activation.DataSource;
51 import javax.xml.bind.JAXBException;
52 import javax.xml.bind.Unmarshaller;
53 import javax.xml.soap.SOAPException;
54 import javax.xml.soap.SOAPMessage;
55 import javax.xml.stream.XMLStreamException;
56 import javax.xml.stream.XMLStreamReader;
57 import javax.xml.stream.XMLStreamWriter;
58 import javax.xml.transform.Source;
59 import javax.xml.transform.stream.StreamSource;
60 import javax.xml.ws.WebServiceException;
62 import java.io.IOException;
63 import java.io.InputStream;
64 import java.io.OutputStream;
66 /**
67 *
68 * @author Jitendra Kotamraju
69 */
70 public final class XMLMessage {
72 private static final int PLAIN_XML_FLAG = 1; // 00001
73 private static final int MIME_MULTIPART_FLAG = 2; // 00010
74 private static final int FI_ENCODED_FLAG = 16; // 10000
76 /*
77 * Construct a message given a content type and an input stream.
78 */
79 public static Message create(final String ct, InputStream in, WSFeatureList f) {
80 Message data;
81 try {
82 in = StreamUtils.hasSomeData(in);
83 if (in == null) {
84 return Messages.createEmpty(SOAPVersion.SOAP_11);
85 }
87 if (ct != null) {
88 final ContentType contentType = new ContentType(ct);
89 final int contentTypeId = identifyContentType(contentType);
90 if ((contentTypeId & MIME_MULTIPART_FLAG) != 0) {
91 data = new XMLMultiPart(ct, in, f);
92 } else if ((contentTypeId & PLAIN_XML_FLAG) != 0) {
93 data = new XmlContent(ct, in, f);
94 } else {
95 data = new UnknownContent(ct, in);
96 }
97 } else {
98 // According to HTTP spec 7.2.1, if the media type remain
99 // unknown, treat as application/octet-stream
100 data = new UnknownContent("application/octet-stream", in);
101 }
102 } catch(Exception ex) {
103 throw new WebServiceException(ex);
104 }
105 return data;
106 }
109 public static Message create(Source source) {
110 return (source == null) ?
111 Messages.createEmpty(SOAPVersion.SOAP_11) :
112 Messages.createUsingPayload(source, SOAPVersion.SOAP_11);
113 }
115 public static Message create(DataSource ds, WSFeatureList f) {
116 try {
117 return (ds == null) ?
118 Messages.createEmpty(SOAPVersion.SOAP_11) :
119 create(ds.getContentType(), ds.getInputStream(), f);
120 } catch(IOException ioe) {
121 throw new WebServiceException(ioe);
122 }
123 }
125 public static Message create(Exception e) {
126 return new FaultMessage(SOAPVersion.SOAP_11);
127 }
129 /*
130 * Get the content type ID from the content type.
131 */
132 private static int getContentId(String ct) {
133 try {
134 final ContentType contentType = new ContentType(ct);
135 return identifyContentType(contentType);
136 } catch(Exception ex) {
137 throw new WebServiceException(ex);
138 }
139 }
141 /**
142 * Return true if the content uses fast infoset.
143 */
144 public static boolean isFastInfoset(String ct) {
145 return (getContentId(ct) & FI_ENCODED_FLAG) != 0;
146 }
148 /*
149 * Verify a contentType.
150 *
151 * @return
152 * MIME_MULTIPART_FLAG | PLAIN_XML_FLAG
153 * MIME_MULTIPART_FLAG | FI_ENCODED_FLAG;
154 * PLAIN_XML_FLAG
155 * FI_ENCODED_FLAG
156 *
157 */
158 public static int identifyContentType(ContentType contentType) {
159 String primary = contentType.getPrimaryType();
160 String sub = contentType.getSubType();
162 if (primary.equalsIgnoreCase("multipart") && sub.equalsIgnoreCase("related")) {
163 String type = contentType.getParameter("type");
164 if (type != null) {
165 if (isXMLType(type)) {
166 return MIME_MULTIPART_FLAG | PLAIN_XML_FLAG;
167 } else if (isFastInfosetType(type)) {
168 return MIME_MULTIPART_FLAG | FI_ENCODED_FLAG;
169 }
170 }
171 return 0;
172 } else if (isXMLType(primary, sub)) {
173 return PLAIN_XML_FLAG;
174 } else if (isFastInfosetType(primary, sub)) {
175 return FI_ENCODED_FLAG;
176 }
177 return 0;
178 }
180 protected static boolean isXMLType(@NotNull String primary, @NotNull String sub) {
181 return (primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml"))
182 || (primary.equalsIgnoreCase("application") && sub.equalsIgnoreCase("xml"))
183 || (primary.equalsIgnoreCase("application") && sub.toLowerCase().endsWith("+xml"));
184 }
186 protected static boolean isXMLType(String type) {
187 String lowerType = type.toLowerCase();
188 return lowerType.startsWith("text/xml")
189 || lowerType.startsWith("application/xml")
190 || (lowerType.startsWith("application/") && (lowerType.indexOf("+xml") != -1));
191 }
193 protected static boolean isFastInfosetType(String primary, String sub) {
194 return primary.equalsIgnoreCase("application") && sub.equalsIgnoreCase("fastinfoset");
195 }
197 protected static boolean isFastInfosetType(String type) {
198 return type.toLowerCase().startsWith("application/fastinfoset");
199 }
202 /**
203 * Access a {@link Message} as a {@link DataSource}.
204 * <p>
205 * A {@link Message} implementation will implement this if the
206 * messages is to be access as data source.
207 * <p>
208 * TODO: consider putting as part of the API.
209 */
210 public static interface MessageDataSource {
211 /**
212 * Check if the data source has been consumed.
213 * @return true of the data source has been consumed, otherwise false.
214 */
215 boolean hasUnconsumedDataSource();
217 /**
218 * Get the data source.
219 * @return the data source.
220 */
221 DataSource getDataSource();
222 }
224 /**
225 * It's conent-type is some XML type
226 *
227 */
228 private static class XmlContent extends AbstractMessageImpl implements MessageDataSource {
229 private final XmlDataSource dataSource;
230 private boolean consumed;
231 private Message delegate;
232 private final HeaderList headerList;
233 // private final WSBinding binding;
234 private WSFeatureList features;
236 public XmlContent(String ct, InputStream in, WSFeatureList f) {
237 super(SOAPVersion.SOAP_11);
238 dataSource = new XmlDataSource(ct, in);
239 this.headerList = new HeaderList(SOAPVersion.SOAP_11);
240 // this.binding = binding;
241 features = f;
242 }
244 private Message getMessage() {
245 if (delegate == null) {
246 InputStream in = dataSource.getInputStream();
247 assert in != null;
248 delegate = Messages.createUsingPayload(new StreamSource(in), SOAPVersion.SOAP_11);
249 consumed = true;
250 }
251 return delegate;
252 }
254 public boolean hasUnconsumedDataSource() {
255 return !dataSource.consumed()&&!consumed;
256 }
258 public DataSource getDataSource() {
259 return hasUnconsumedDataSource() ? dataSource :
260 XMLMessage.getDataSource(getMessage(), features);
261 }
263 public boolean hasHeaders() {
264 return false;
265 }
267 public @NotNull MessageHeaders getHeaders() {
268 return headerList;
269 }
271 public String getPayloadLocalPart() {
272 return getMessage().getPayloadLocalPart();
273 }
275 public String getPayloadNamespaceURI() {
276 return getMessage().getPayloadNamespaceURI();
277 }
279 public boolean hasPayload() {
280 return true;
281 }
283 public boolean isFault() {
284 return false;
285 }
287 public Source readEnvelopeAsSource() {
288 return getMessage().readEnvelopeAsSource();
289 }
291 public Source readPayloadAsSource() {
292 return getMessage().readPayloadAsSource();
293 }
295 public SOAPMessage readAsSOAPMessage() throws SOAPException {
296 return getMessage().readAsSOAPMessage();
297 }
299 public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound) throws SOAPException {
300 return getMessage().readAsSOAPMessage(packet, inbound);
301 }
303 public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
304 return (T)getMessage().readPayloadAsJAXB(unmarshaller);
305 }
306 /** @deprecated */
307 public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
308 return getMessage().readPayloadAsJAXB(bridge);
309 }
311 public XMLStreamReader readPayload() throws XMLStreamException {
312 return getMessage().readPayload();
313 }
316 public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException {
317 getMessage().writePayloadTo(sw);
318 }
320 public void writeTo(XMLStreamWriter sw) throws XMLStreamException {
321 getMessage().writeTo(sw);
322 }
324 public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler) throws SAXException {
325 getMessage().writeTo(contentHandler, errorHandler);
326 }
328 public Message copy() {
329 return getMessage().copy();
330 }
332 protected void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
333 throw new UnsupportedOperationException();
334 }
336 }
340 /**
341 * Data represented as a multi-part MIME message.
342 * <p>
343 * The root part may be an XML or an FI document. This class
344 * parses MIME message lazily.
345 */
346 public static final class XMLMultiPart extends AbstractMessageImpl implements MessageDataSource {
347 private final DataSource dataSource;
348 private final StreamingAttachmentFeature feature;
349 private Message delegate;
350 private HeaderList headerList;// = new HeaderList();
351 // private final WSBinding binding;
352 private final WSFeatureList features;
354 public XMLMultiPart(final String contentType, final InputStream is, WSFeatureList f) {
355 super(SOAPVersion.SOAP_11);
356 headerList = new HeaderList(SOAPVersion.SOAP_11);
357 dataSource = createDataSource(contentType, is);
358 this.feature = f.get(StreamingAttachmentFeature.class);
359 this.features = f;
360 }
362 private Message getMessage() {
363 if (delegate == null) {
364 MimeMultipartParser mpp;
365 try {
366 mpp = new MimeMultipartParser(dataSource.getInputStream(),
367 dataSource.getContentType(), feature);
368 } catch(IOException ioe) {
369 throw new WebServiceException(ioe);
370 }
371 InputStream in = mpp.getRootPart().asInputStream();
372 assert in != null;
373 delegate = new PayloadSourceMessage(headerList, new StreamSource(in), new MimeAttachmentSet(mpp), SOAPVersion.SOAP_11);
374 }
375 return delegate;
376 }
378 public boolean hasUnconsumedDataSource() {
379 return delegate == null;
380 }
382 public DataSource getDataSource() {
383 return hasUnconsumedDataSource() ? dataSource :
384 XMLMessage.getDataSource(getMessage(), features);
385 }
387 public boolean hasHeaders() {
388 return false;
389 }
391 public @NotNull MessageHeaders getHeaders() {
392 return headerList;
393 }
395 public String getPayloadLocalPart() {
396 return getMessage().getPayloadLocalPart();
397 }
399 public String getPayloadNamespaceURI() {
400 return getMessage().getPayloadNamespaceURI();
401 }
403 public boolean hasPayload() {
404 return true;
405 }
407 public boolean isFault() {
408 return false;
409 }
411 public Source readEnvelopeAsSource() {
412 return getMessage().readEnvelopeAsSource();
413 }
415 public Source readPayloadAsSource() {
416 return getMessage().readPayloadAsSource();
417 }
419 public SOAPMessage readAsSOAPMessage() throws SOAPException {
420 return getMessage().readAsSOAPMessage();
421 }
423 public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound) throws SOAPException {
424 return getMessage().readAsSOAPMessage(packet, inbound);
425 }
427 public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
428 return (T)getMessage().readPayloadAsJAXB(unmarshaller);
429 }
431 public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
432 return getMessage().readPayloadAsJAXB(bridge);
433 }
435 public XMLStreamReader readPayload() throws XMLStreamException {
436 return getMessage().readPayload();
437 }
439 public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException {
440 getMessage().writePayloadTo(sw);
441 }
443 public void writeTo(XMLStreamWriter sw) throws XMLStreamException {
444 getMessage().writeTo(sw);
445 }
447 public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler) throws SAXException {
448 getMessage().writeTo(contentHandler, errorHandler);
449 }
451 public Message copy() {
452 return getMessage().copy();
453 }
455 protected void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
456 throw new UnsupportedOperationException();
457 }
459 @Override
460 public boolean isOneWay(@NotNull WSDLPort port) {
461 return false;
462 }
464 public @NotNull AttachmentSet getAttachments() {
465 return getMessage().getAttachments();
466 }
468 }
470 private static class FaultMessage extends EmptyMessageImpl {
472 public FaultMessage(SOAPVersion version) {
473 super(version);
474 }
476 @Override
477 public boolean isFault() {
478 return true;
479 }
480 }
483 /**
484 * Don't know about this content. It's conent-type is NOT the XML types
485 * we recognize(text/xml, application/xml, multipart/related;text/xml etc).
486 *
487 * This could be used to represent image/jpeg etc
488 */
489 public static class UnknownContent extends AbstractMessageImpl implements MessageDataSource {
490 private final DataSource ds;
491 private final HeaderList headerList;
493 public UnknownContent(final String ct, final InputStream in) {
494 this(createDataSource(ct,in));
495 }
497 public UnknownContent(DataSource ds) {
498 super(SOAPVersion.SOAP_11);
499 this.ds = ds;
500 this.headerList = new HeaderList(SOAPVersion.SOAP_11);
501 }
503 /*
504 * Copy constructor.
505 */
506 private UnknownContent(UnknownContent that) {
507 super(that.soapVersion);
508 this.ds = that.ds;
509 this.headerList = HeaderList.copy(that.headerList);
510 }
512 public boolean hasUnconsumedDataSource() {
513 return true;
514 }
516 public DataSource getDataSource() {
517 assert ds != null;
518 return ds;
519 }
521 protected void writePayloadTo(ContentHandler contentHandler,
522 ErrorHandler errorHandler, boolean fragment) throws SAXException {
523 throw new UnsupportedOperationException();
524 }
526 public boolean hasHeaders() {
527 return false;
528 }
530 public boolean isFault() {
531 return false;
532 }
534 public MessageHeaders getHeaders() {
535 return headerList;
536 }
538 public String getPayloadLocalPart() {
539 throw new UnsupportedOperationException();
540 }
542 public String getPayloadNamespaceURI() {
543 throw new UnsupportedOperationException();
544 }
546 public boolean hasPayload() {
547 return false;
548 }
550 public Source readPayloadAsSource() {
551 return null;
552 }
554 public XMLStreamReader readPayload() throws XMLStreamException {
555 throw new WebServiceException("There isn't XML payload. Shouldn't come here.");
556 }
558 public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException {
559 // No XML. Nothing to do
560 }
562 public Message copy() {
563 return new UnknownContent(this);
564 }
566 }
568 public static DataSource getDataSource(Message msg, WSFeatureList f) {
569 if (msg == null)
570 return null;
571 if (msg instanceof MessageDataSource) {
572 return ((MessageDataSource)msg).getDataSource();
573 } else {
574 AttachmentSet atts = msg.getAttachments();
575 if (atts != null && !atts.isEmpty()) {
576 final ByteArrayBuffer bos = new ByteArrayBuffer();
577 try {
578 Codec codec = new XMLHTTPBindingCodec(f);
579 Packet packet = new Packet(msg);
580 com.sun.xml.internal.ws.api.pipe.ContentType ct = codec.getStaticContentType(packet);
581 codec.encode(packet, bos);
582 return createDataSource(ct.getContentType(), bos.newInputStream());
583 } catch(IOException ioe) {
584 throw new WebServiceException(ioe);
585 }
587 } else {
588 final ByteArrayBuffer bos = new ByteArrayBuffer();
589 XMLStreamWriter writer = XMLStreamWriterFactory.create(bos);
590 try {
591 msg.writePayloadTo(writer);
592 writer.flush();
593 } catch (XMLStreamException e) {
594 throw new WebServiceException(e);
595 }
596 return XMLMessage.createDataSource("text/xml", bos.newInputStream());
597 }
598 }
599 }
601 public static DataSource createDataSource(final String contentType, final InputStream is) {
602 return new XmlDataSource(contentType, is);
603 }
605 private static class XmlDataSource implements DataSource {
606 private final String contentType;
607 private final InputStream is;
608 private boolean consumed;
610 XmlDataSource(String contentType, final InputStream is) {
611 this.contentType = contentType;
612 this.is = is;
613 }
615 public boolean consumed() {
616 return consumed;
617 }
619 public InputStream getInputStream() {
620 consumed = !consumed;
621 return is;
622 }
624 public OutputStream getOutputStream() {
625 return null;
626 }
628 public String getContentType() {
629 return contentType;
630 }
632 public String getName() {
633 return "";
634 }
635 }
636 }