|
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 */ |
|
25 |
|
26 package com.sun.xml.internal.ws.api.message; |
|
27 |
|
28 import com.sun.istack.internal.NotNull; |
|
29 import com.sun.istack.internal.Nullable; |
|
30 import com.sun.xml.internal.bind.api.Bridge; |
|
31 import com.sun.xml.internal.ws.api.BindingID; |
|
32 import com.sun.xml.internal.ws.api.SOAPVersion; |
|
33 import com.sun.xml.internal.ws.api.WSBinding; |
|
34 import com.sun.xml.internal.ws.api.addressing.AddressingVersion; |
|
35 import com.sun.xml.internal.ws.api.model.JavaMethod; |
|
36 import com.sun.xml.internal.ws.api.model.SEIModel; |
|
37 import com.sun.xml.internal.ws.api.model.WSDLOperationMapping; |
|
38 import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundOperation; |
|
39 import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundPortType; |
|
40 import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; |
|
41 import com.sun.xml.internal.ws.api.pipe.Codec; |
|
42 import com.sun.xml.internal.ws.api.pipe.Pipe; |
|
43 import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory; |
|
44 import com.sun.xml.internal.ws.client.dispatch.DispatchImpl; |
|
45 import com.sun.xml.internal.ws.message.AttachmentSetImpl; |
|
46 import com.sun.xml.internal.ws.message.StringHeader; |
|
47 import com.sun.xml.internal.ws.message.jaxb.JAXBMessage; |
|
48 import com.sun.xml.internal.ws.spi.db.XMLBridge; |
|
49 import com.sun.xml.internal.ws.fault.SOAPFaultBuilder; |
|
50 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx; |
|
51 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx; |
|
52 import org.xml.sax.ContentHandler; |
|
53 import org.xml.sax.ErrorHandler; |
|
54 import org.xml.sax.SAXException; |
|
55 import org.xml.sax.SAXParseException; |
|
56 |
|
57 import javax.xml.bind.JAXBException; |
|
58 import javax.xml.bind.Unmarshaller; |
|
59 import javax.xml.namespace.QName; |
|
60 import javax.xml.soap.MimeHeaders; |
|
61 import javax.xml.soap.SOAPException; |
|
62 import javax.xml.soap.SOAPMessage; |
|
63 import javax.xml.stream.XMLStreamException; |
|
64 import javax.xml.stream.XMLStreamReader; |
|
65 import javax.xml.stream.XMLStreamWriter; |
|
66 import javax.xml.transform.Source; |
|
67 import javax.xml.ws.Dispatch; |
|
68 import javax.xml.ws.WebServiceException; |
|
69 import java.io.InputStream; |
|
70 import java.lang.reflect.Method; |
|
71 import java.lang.reflect.Proxy; |
|
72 import java.util.List; |
|
73 import java.util.Map; |
|
74 import java.util.UUID; |
|
75 |
|
76 /** |
|
77 * Represents a SOAP message. |
|
78 * |
|
79 * |
|
80 * <h2>What is a message?</h2> |
|
81 * <p> |
|
82 * A {@link Message} consists of the following: |
|
83 * |
|
84 * <ol> |
|
85 * <li> |
|
86 * Random-accessible list of headers. |
|
87 * a header is a representation of an element inside |
|
88 * <soap:Header>. |
|
89 * It can be read multiple times, |
|
90 * can be added or removed, but it is not modifiable. |
|
91 * See {@link HeaderList} for more about headers. |
|
92 * |
|
93 * <li> |
|
94 * The payload of the message, which is a representation |
|
95 * of an element inside <soap:Body>. |
|
96 * the payload is streamed, and therefore it can be |
|
97 * only read once (or can be only written to something once.) |
|
98 * once a payload is used, a message is said to be <b>consumed</b>. |
|
99 * A message {@link #hasPayload() may not have any payload.} |
|
100 * |
|
101 * <li> |
|
102 * Attachments. |
|
103 * TODO: can attachments be streamed? I suspect so. |
|
104 * does anyone need to read attachment twice? |
|
105 * |
|
106 * </ol> |
|
107 * |
|
108 * |
|
109 * <h2>How does this abstraction work?</h2> |
|
110 * <p> |
|
111 * The basic idea behind the {@link Message} is to hide the actual |
|
112 * data representation. For example, a {@link Message} might be |
|
113 * constructed on top of an {@link InputStream} from the accepted HTTP connection, |
|
114 * or it might be constructed on top of a JAXB object as a result |
|
115 * of the method invocation through {@link Proxy}. There will be |
|
116 * a {@link Message} implementation for each of those cases. |
|
117 * |
|
118 * <p> |
|
119 * This interface provides a lot of methods that access the payload |
|
120 * in many different forms, and implementations can implement those |
|
121 * methods in the best possible way. |
|
122 * |
|
123 * <p> |
|
124 * A particular attention is paid to make sure that a {@link Message} |
|
125 * object can be constructed on a stream that is not fully read yet. |
|
126 * We believe this improves the turn-around time on the server side. |
|
127 * |
|
128 * <p> |
|
129 * It is often useful to wrap a {@link Message} into another {@link Message}, |
|
130 * for example to encrypt the body, or to verify the signature as the body |
|
131 * is read. |
|
132 * |
|
133 * <p> |
|
134 * This representation is also used for a REST-ful XML message. |
|
135 * In such case we'll construct a {@link Message} with empty |
|
136 * attachments and headers, and when serializing all headers |
|
137 * and attachments will be ignored. |
|
138 * |
|
139 * |
|
140 * |
|
141 * <h2>Message and XOP</h2> |
|
142 * <p> |
|
143 * XOP is considered as an {@link Codec}, and therefore when you are looking at |
|
144 * {@link Message}, you'll never see <xop:Include> or any such elements |
|
145 * (instead you'll see the base64 data inlined.) If a consumer of infoset isn't |
|
146 * interested in handling XOP by himself, this allows him to work with XOP |
|
147 * correctly even without noticing it. |
|
148 * |
|
149 * <p> |
|
150 * For producers and consumers that are interested in accessing the binary data |
|
151 * more efficiently, they can use {@link XMLStreamReaderEx} and |
|
152 * {@link XMLStreamWriterEx}. |
|
153 * |
|
154 * |
|
155 * |
|
156 * <h2>Message lifespan</h2> |
|
157 * <p> |
|
158 * Often {@link Packet} include information local to a particular |
|
159 * invocaion (such as {@code HttpServletRequest}, from this angle, it makes sense |
|
160 * to tie a lifespan of a message to one pipeline invocation. |
|
161 * <p> |
|
162 * On the other hand, if you think about WS-RM, it often needs to hold on to |
|
163 * a message longer than a pipeline invocation (you might get an HTTP request, |
|
164 * get a message X, get a second HTTP request, get another message Y, and |
|
165 * only then you might want to process X.) |
|
166 * <p> |
|
167 * TODO: what do we do about this? |
|
168 * |
|
169 * |
|
170 * <pre> |
|
171 * TODO: can body element have foreign attributes? maybe ID for security? |
|
172 * Yes, when the SOAP body is signed there will be an ID attribute present |
|
173 * But in this case any security based impl may need access |
|
174 * to the concrete representation. |
|
175 * TODO: HTTP headers? |
|
176 * Yes. Abstracted as transport-based properties. |
|
177 * TODO: who handles SOAP 1.1 and SOAP 1.2 difference? |
|
178 * As separate channel implementations responsible for the creation of the |
|
179 * message? |
|
180 * TODO: session? |
|
181 * TODO: Do we need to expose SOAPMessage explicitly? |
|
182 * SOAPMessage could be the concrete representation but is it necessary to |
|
183 * transform between different concrete representations? |
|
184 * Perhaps this comes down to how use channels for creation and processing. |
|
185 * TODO: Do we need to distinguish better between creation and processing? |
|
186 * Do we really need the requirement that a created message can be resused |
|
187 * for processing. Shall we bifurcate? |
|
188 * |
|
189 * TODO: SOAP version issue |
|
190 * SOAP version is determined by the context, so message itself doesn't carry it around (?) |
|
191 * |
|
192 * TODO: wrapping message needs easier. in particular properties and attachments. |
|
193 * </pre> |
|
194 * |
|
195 * @author Kohsuke Kawaguchi |
|
196 */ |
|
197 public abstract class Message { |
|
198 |
|
199 /** |
|
200 * Returns true if headers are present in the message. |
|
201 * |
|
202 * @return |
|
203 * true if headers are present. |
|
204 */ |
|
205 public abstract boolean hasHeaders(); |
|
206 |
|
207 /** |
|
208 * Gets all the headers of this message. |
|
209 * |
|
210 * <h3>Implementation Note</h3> |
|
211 * <p> |
|
212 * {@link Message} implementation is allowed to defer |
|
213 * the construction of {@link MessageHeaders} object. So |
|
214 * if you only want to check for the existence of any header |
|
215 * element, use {@link #hasHeaders()}. |
|
216 * |
|
217 * @return |
|
218 * always return the same non-null object. |
|
219 */ |
|
220 public abstract @NotNull MessageHeaders getHeaders(); |
|
221 |
|
222 /** |
|
223 * Gets the attachments of this message |
|
224 * (attachments live outside a message.) |
|
225 */ |
|
226 public @NotNull AttachmentSet getAttachments() { |
|
227 if (attachmentSet == null) { |
|
228 attachmentSet = new AttachmentSetImpl(); |
|
229 } |
|
230 return attachmentSet; |
|
231 } |
|
232 |
|
233 /** |
|
234 * Optimization hint for the derived class to check |
|
235 * if we may have some attachments. |
|
236 */ |
|
237 protected boolean hasAttachments() { |
|
238 return attachmentSet!=null; |
|
239 } |
|
240 |
|
241 protected AttachmentSet attachmentSet; |
|
242 |
|
243 private WSDLBoundOperation operation = null; |
|
244 |
|
245 private WSDLOperationMapping wsdlOperationMapping = null; |
|
246 |
|
247 private MessageMetadata messageMetadata = null; |
|
248 |
|
249 public void setMessageMedadata(MessageMetadata metadata) { |
|
250 messageMetadata = metadata; |
|
251 } |
|
252 |
|
253 |
|
254 /** |
|
255 * Returns the operation of which this message is an instance of. |
|
256 * |
|
257 * <p> |
|
258 * This method relies on {@link WSDLBoundPortType#getOperation(String, String)} but |
|
259 * it does so in an efficient way. |
|
260 * |
|
261 * @deprecated It is not always possible to uniquely identify the WSDL Operation from just the |
|
262 * information in the Message. Instead, Use {@link com.sun.xml.internal.ws.api.message.Packet#getWSDLOperation()} |
|
263 * to get it correctly. |
|
264 * |
|
265 * <p> |
|
266 * This method works only for a request. A pipe can determine an operation for a request, |
|
267 * and then keep it in a local variable to use it with a response, so there should be |
|
268 * no need to find out operation from a response (besides, there might not be any response!). |
|
269 * |
|
270 * @param boundPortType |
|
271 * This represents the port for which this message is used. |
|
272 * Most {@link Pipe}s should get this information when they are created, |
|
273 * since a pippeline always work against a particular type of {@link WSDLPort}. |
|
274 * |
|
275 * @return |
|
276 * Null if the operation was not found. This is possible, for example when a protocol |
|
277 * message is sent through a pipeline, or when we receive an invalid request on the server, |
|
278 * or when we are on the client and the user appliation sends a random DOM through |
|
279 * {@link Dispatch}, so this error needs to be handled gracefully. |
|
280 */ |
|
281 @Deprecated |
|
282 public final @Nullable WSDLBoundOperation getOperation(@NotNull WSDLBoundPortType boundPortType) { |
|
283 if (operation == null && messageMetadata != null) { |
|
284 if (wsdlOperationMapping == null) wsdlOperationMapping = messageMetadata.getWSDLOperationMapping(); |
|
285 if (wsdlOperationMapping != null) operation = wsdlOperationMapping.getWSDLBoundOperation(); |
|
286 } |
|
287 if(operation==null) |
|
288 operation = boundPortType.getOperation(getPayloadNamespaceURI(),getPayloadLocalPart()); |
|
289 return operation; |
|
290 } |
|
291 |
|
292 /** |
|
293 * The same as {@link #getOperation(WSDLBoundPortType)} but |
|
294 * takes {@link WSDLPort} for convenience. |
|
295 * |
|
296 * @deprecated It is not always possible to uniquely identify the WSDL Operation from just the |
|
297 * information in the Message. Instead, Use {@link com.sun.xml.internal.ws.api.message.Packet#getWSDLOperation()} |
|
298 * to get it correctly. |
|
299 */ |
|
300 @Deprecated |
|
301 public final @Nullable WSDLBoundOperation getOperation(@NotNull WSDLPort port) { |
|
302 return getOperation(port.getBinding()); |
|
303 } |
|
304 |
|
305 /** |
|
306 * Returns the java Method of which this message is an instance of. |
|
307 * |
|
308 * It is not always possible to uniquely identify the WSDL Operation from just the |
|
309 * information in the Message. Instead, Use {@link com.sun.xml.internal.ws.api.message.Packet#getWSDLOperation()} |
|
310 * to get the QName of the associated wsdl operation correctly. |
|
311 * |
|
312 * <p> |
|
313 * This method works only for a request. A pipe can determine a {@link Method} |
|
314 * for a request, and then keep it in a local variable to use it with a response, |
|
315 * so there should be no need to find out operation from a response (besides, |
|
316 * there might not be any response!). |
|
317 * |
|
318 * @param seiModel |
|
319 * This represents the java model for the endpoint |
|
320 * Some server {@link Pipe}s would get this information when they are created. |
|
321 * |
|
322 * @return |
|
323 * Null if there is no corresponding Method for this message. This is |
|
324 * possible, for example when a protocol message is sent through a |
|
325 * pipeline, or when we receive an invalid request on the server, |
|
326 * or when we are on the client and the user appliation sends a random |
|
327 * DOM through {@link Dispatch}, so this error needs to be handled |
|
328 * gracefully. |
|
329 */ |
|
330 @Deprecated |
|
331 public final @Nullable JavaMethod getMethod(@NotNull SEIModel seiModel) { |
|
332 if (wsdlOperationMapping == null && messageMetadata != null) { |
|
333 wsdlOperationMapping = messageMetadata.getWSDLOperationMapping(); |
|
334 } |
|
335 if (wsdlOperationMapping != null) { |
|
336 return wsdlOperationMapping.getJavaMethod(); |
|
337 } |
|
338 //fall back to the original logic which could be incorrect ... |
|
339 String localPart = getPayloadLocalPart(); |
|
340 String nsUri; |
|
341 if (localPart == null) { |
|
342 localPart = ""; |
|
343 nsUri = ""; |
|
344 } else { |
|
345 nsUri = getPayloadNamespaceURI(); |
|
346 } |
|
347 QName name = new QName(nsUri, localPart); |
|
348 return seiModel.getJavaMethod(name); |
|
349 } |
|
350 |
|
351 private Boolean isOneWay; |
|
352 |
|
353 /** |
|
354 * Returns true if this message is a request message for a |
|
355 * one way operation according to the given WSDL. False otherwise. |
|
356 * |
|
357 * <p> |
|
358 * This method is functionally equivalent as doing |
|
359 * {@code getOperation(port).getOperation().isOneWay()} |
|
360 * (with proper null check and all.) But this method |
|
361 * can sometimes work faster than that (for example, |
|
362 * on the client side when used with SEI.) |
|
363 * |
|
364 * @param port |
|
365 * {@link Message}s are always created under the context of |
|
366 * one {@link WSDLPort} and they never go outside that context. |
|
367 * Pass in that "governing" {@link WSDLPort} object here. |
|
368 * We chose to receive this as a parameter instead of |
|
369 * keeping {@link WSDLPort} in a message, just to save the storage. |
|
370 * |
|
371 * <p> |
|
372 * The implementation of this method involves caching the return |
|
373 * value, so the behavior is undefined if multiple callers provide |
|
374 * different {@link WSDLPort} objects, which is a bug of the caller. |
|
375 */ |
|
376 public boolean isOneWay(@NotNull WSDLPort port) { |
|
377 if(isOneWay==null) { |
|
378 // we don't know, so compute. |
|
379 WSDLBoundOperation op = getOperation(port); |
|
380 if(op!=null) |
|
381 isOneWay = op.getOperation().isOneWay(); |
|
382 else |
|
383 // the contract is to return true only when it's known to be one way. |
|
384 isOneWay = false; |
|
385 } |
|
386 return isOneWay; |
|
387 } |
|
388 |
|
389 /** |
|
390 * Makes an assertion that this {@link Message} is |
|
391 * a request message for an one-way operation according |
|
392 * to the context WSDL. |
|
393 * |
|
394 * <p> |
|
395 * This method is really only intended to be invoked from within |
|
396 * the JAX-WS runtime, and not by any code building on top of it. |
|
397 * |
|
398 * <p> |
|
399 * This method can be invoked only when the caller "knows" what |
|
400 * WSDL says. Also, there's no point in invoking this method if the caller |
|
401 * is doing {@code getOperation(port).getOperation().isOneWay()}, |
|
402 * or sniffing the payload tag name. |
|
403 * In particular, this includes {@link DispatchImpl}. |
|
404 * |
|
405 * <p> |
|
406 * Once called, this allows {@link #isOneWay(WSDLPort)} method |
|
407 * to return a value quickly. |
|
408 * |
|
409 * @see #isOneWay(WSDLPort) |
|
410 */ |
|
411 public final void assertOneWay(boolean value) { |
|
412 // if two callers make different assertions, that's a bug. |
|
413 // this is an assertion, not a runtime check because |
|
414 // nobody outside JAX-WS should be using this. |
|
415 assert isOneWay==null || isOneWay==value; |
|
416 |
|
417 isOneWay = value; |
|
418 } |
|
419 |
|
420 |
|
421 /** |
|
422 * Gets the local name of the payload element. |
|
423 * |
|
424 * @return |
|
425 * null if a {@link Message} doesn't have any payload. |
|
426 */ |
|
427 public abstract @Nullable String getPayloadLocalPart(); |
|
428 |
|
429 /** |
|
430 * Gets the namespace URI of the payload element. |
|
431 * |
|
432 * @return |
|
433 * null if a {@link Message} doesn't have any payload. |
|
434 */ |
|
435 public abstract String getPayloadNamespaceURI(); |
|
436 // I'm not putting @Nullable on it because doing null check on getPayloadLocalPart() should be suffice |
|
437 |
|
438 /** |
|
439 * Returns true if a {@link Message} has a payload. |
|
440 * |
|
441 * <p> |
|
442 * A message without a payload is a SOAP message that looks like: |
|
443 * <pre><xmp> |
|
444 * <S:Envelope> |
|
445 * <S:Header> |
|
446 * ... |
|
447 * </S:Header> |
|
448 * <S:Body /> |
|
449 * </S:Envelope> |
|
450 * </xmp></pre> |
|
451 */ |
|
452 public abstract boolean hasPayload(); |
|
453 |
|
454 /** |
|
455 * Returns true if this message is a fault. |
|
456 * |
|
457 * <p> |
|
458 * Just a convenience method built on {@link #getPayloadNamespaceURI()} |
|
459 * and {@link #getPayloadLocalPart()}. |
|
460 */ |
|
461 public boolean isFault() { |
|
462 // TODO: is SOAP version a property of a Message? |
|
463 // or is it defined by external factors? |
|
464 // how do I compare? |
|
465 String localPart = getPayloadLocalPart(); |
|
466 if(localPart==null || !localPart.equals("Fault")) |
|
467 return false; |
|
468 |
|
469 String nsUri = getPayloadNamespaceURI(); |
|
470 return nsUri.equals(SOAPVersion.SOAP_11.nsUri) || nsUri.equals(SOAPVersion.SOAP_12.nsUri); |
|
471 } |
|
472 |
|
473 /** |
|
474 * It gives S:Envelope/S:Body/S:Fault/detail 's first child's name. Should |
|
475 * be called for messages that have SOAP Fault. |
|
476 * |
|
477 * <p> This implementation is expensive so concrete implementations are |
|
478 * expected to override this one. |
|
479 * |
|
480 * @return first detail entry's name, if there is one |
|
481 * else null |
|
482 */ |
|
483 public @Nullable QName getFirstDetailEntryName() { |
|
484 assert isFault(); |
|
485 Message msg = copy(); |
|
486 try { |
|
487 SOAPFaultBuilder fault = SOAPFaultBuilder.create(msg); |
|
488 return fault.getFirstDetailEntryName(); |
|
489 } catch (JAXBException e) { |
|
490 throw new WebServiceException(e); |
|
491 } |
|
492 } |
|
493 |
|
494 /** |
|
495 * Consumes this message including the envelope. |
|
496 * returns it as a {@link Source} object. |
|
497 */ |
|
498 public abstract Source readEnvelopeAsSource(); |
|
499 |
|
500 |
|
501 /** |
|
502 * Returns the payload as a {@link Source} object. |
|
503 * |
|
504 * This consumes the message. |
|
505 * |
|
506 * @return |
|
507 * if there's no payload, this method returns null. |
|
508 */ |
|
509 public abstract Source readPayloadAsSource(); |
|
510 |
|
511 /** |
|
512 * Creates the equivalent {@link SOAPMessage} from this message. |
|
513 * |
|
514 * This consumes the message. |
|
515 * |
|
516 * @throws SOAPException |
|
517 * if there's any error while creating a {@link SOAPMessage}. |
|
518 */ |
|
519 public abstract SOAPMessage readAsSOAPMessage() throws SOAPException; |
|
520 |
|
521 /** |
|
522 * Creates the equivalent {@link SOAPMessage} from this message. It also uses |
|
523 * transport specific headers from Packet during the SOAPMessage construction |
|
524 * so that {@link SOAPMessage#getMimeHeaders()} gives meaningful transport |
|
525 * headers. |
|
526 * |
|
527 * This consumes the message. |
|
528 * |
|
529 * @throws SOAPException |
|
530 * if there's any error while creating a {@link SOAPMessage}. |
|
531 */ |
|
532 public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound) throws SOAPException { |
|
533 return readAsSOAPMessage(); |
|
534 } |
|
535 |
|
536 public static Map<String, List<String>> getTransportHeaders(Packet packet) { |
|
537 return getTransportHeaders(packet, packet.getState().isInbound()); |
|
538 } |
|
539 |
|
540 public static Map<String, List<String>> getTransportHeaders(Packet packet, boolean inbound) { |
|
541 Map<String, List<String>> headers = null; |
|
542 String key = inbound ? Packet.INBOUND_TRANSPORT_HEADERS : Packet.OUTBOUND_TRANSPORT_HEADERS; |
|
543 if (packet.supports(key)) { |
|
544 headers = (Map<String, List<String>>)packet.get(key); |
|
545 } |
|
546 return headers; |
|
547 } |
|
548 |
|
549 public static void addSOAPMimeHeaders(MimeHeaders mh, Map<String, List<String>> headers) { |
|
550 for(Map.Entry<String, List<String>> e : headers.entrySet()) { |
|
551 if (!e.getKey().equalsIgnoreCase("Content-Type")) { |
|
552 for(String value : e.getValue()) { |
|
553 mh.addHeader(e.getKey(), value); |
|
554 } |
|
555 } |
|
556 } |
|
557 } |
|
558 /** |
|
559 * Reads the payload as a JAXB object by using the given unmarshaller. |
|
560 * |
|
561 * This consumes the message. |
|
562 * |
|
563 * @throws JAXBException |
|
564 * If JAXB reports an error during the processing. |
|
565 */ |
|
566 public abstract <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException; |
|
567 |
|
568 /** |
|
569 * Reads the payload as a JAXB object according to the given {@link Bridge}. |
|
570 * |
|
571 * This consumes the message. |
|
572 * |
|
573 * @deprecated |
|
574 * @return null |
|
575 * if there's no payload. |
|
576 * @throws JAXBException |
|
577 * If JAXB reports an error during the processing. |
|
578 */ |
|
579 public abstract <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException; |
|
580 |
|
581 /** |
|
582 * Reads the payload as a Data-Bond object |
|
583 * |
|
584 * This consumes the message. |
|
585 * |
|
586 * @return null |
|
587 * if there's no payload. |
|
588 * @throws JAXBException |
|
589 * If JAXB reports an error during the processing. |
|
590 */ |
|
591 public abstract <T> T readPayloadAsJAXB(XMLBridge<T> bridge) throws JAXBException; |
|
592 |
|
593 /** |
|
594 * Reads the payload as a {@link XMLStreamReader} |
|
595 * |
|
596 * This consumes the message. The caller is encouraged to call |
|
597 * {@link XMLStreamReaderFactory#recycle(XMLStreamReader)} when finished using |
|
598 * the instance. |
|
599 * |
|
600 * @return |
|
601 * If there's no payload, this method returns null. |
|
602 * Otherwise always non-null valid {@link XMLStreamReader} that points to |
|
603 * the payload tag name. |
|
604 */ |
|
605 public abstract XMLStreamReader readPayload() throws XMLStreamException; |
|
606 |
|
607 /** |
|
608 * Marks the message as consumed, without actually reading the contents. |
|
609 * |
|
610 * <p> |
|
611 * This method provides an opportunity for implementations to reuse |
|
612 * any reusable resources needed for representing the payload. |
|
613 * |
|
614 * <p> |
|
615 * This method may not be called more than once since it may have |
|
616 * released the reusable resources. |
|
617 */ |
|
618 public void consume() {} |
|
619 |
|
620 /** |
|
621 * Writes the payload to StAX. |
|
622 * |
|
623 * This method writes just the payload of the message to the writer. |
|
624 * This consumes the message. |
|
625 * The implementation will not write |
|
626 * {@link XMLStreamWriter#writeStartDocument()} |
|
627 * nor |
|
628 * {@link XMLStreamWriter#writeEndDocument()} |
|
629 * |
|
630 * <p> |
|
631 * If there's no payload, this method is no-op. |
|
632 * |
|
633 * @throws XMLStreamException |
|
634 * If the {@link XMLStreamWriter} reports an error, |
|
635 * or some other errors happen during the processing. |
|
636 */ |
|
637 public abstract void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException; |
|
638 |
|
639 /** |
|
640 * Writes the whole SOAP message (but not attachments) |
|
641 * to the given writer. |
|
642 * |
|
643 * This consumes the message. |
|
644 * |
|
645 * @throws XMLStreamException |
|
646 * If the {@link XMLStreamWriter} reports an error, |
|
647 * or some other errors happen during the processing. |
|
648 */ |
|
649 public abstract void writeTo(XMLStreamWriter sw) throws XMLStreamException; |
|
650 |
|
651 /** |
|
652 * Writes the whole SOAP envelope as SAX events. |
|
653 * |
|
654 * <p> |
|
655 * This consumes the message. |
|
656 * |
|
657 * @param contentHandler |
|
658 * must not be nulll. |
|
659 * @param errorHandler |
|
660 * must not be null. |
|
661 * any error encountered during the SAX event production must be |
|
662 * first reported to this error handler. Fatal errors can be then |
|
663 * thrown as {@link SAXParseException}. {@link SAXException}s thrown |
|
664 * from {@link ErrorHandler} should propagate directly through this method. |
|
665 */ |
|
666 public abstract void writeTo( ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException; |
|
667 |
|
668 // TODO: do we need a method that reads payload as a fault? |
|
669 // do we want a separte streaming representation of fault? |
|
670 // or would SOAPFault in SAAJ do? |
|
671 |
|
672 |
|
673 |
|
674 /** |
|
675 * Creates a copy of a {@link Message}. |
|
676 * |
|
677 * <p> |
|
678 * This method creates a new {@link Message} whose header/payload/attachments/properties |
|
679 * are identical to this {@link Message}. Once created, the created {@link Message} |
|
680 * and the original {@link Message} behaves independently --- adding header/ |
|
681 * attachment to one {@link Message} doesn't affect another {@link Message} |
|
682 * at all. |
|
683 * |
|
684 * <p> |
|
685 * This method does <b>NOT</b> consume a message. |
|
686 * |
|
687 * <p> |
|
688 * To enable efficient copy operations, there's a few restrictions on |
|
689 * how copied message can be used. |
|
690 * |
|
691 * <ol> |
|
692 * <li>The original and the copy may not be |
|
693 * used concurrently by two threads (this allows two {@link Message}s |
|
694 * to share some internal resources, such as JAXB marshallers.) |
|
695 * Note that it's OK for the original and the copy to be processed |
|
696 * by two threads, as long as they are not concurrent. |
|
697 * |
|
698 * <li>The copy has the same 'life scope' |
|
699 * as the original (this allows shallower copy, such as |
|
700 * JAXB beans wrapped in {@link JAXBMessage}.) |
|
701 * </ol> |
|
702 * |
|
703 * <p> |
|
704 * A 'life scope' of a message created during a message processing |
|
705 * in a pipeline is until a pipeline processes the next message. |
|
706 * A message cannot be kept beyond its life scope. |
|
707 * |
|
708 * (This experimental design is to allow message objects to be reused |
|
709 * --- feedback appreciated.) |
|
710 * |
|
711 * |
|
712 * |
|
713 * <h3>Design Rationale</h3> |
|
714 * <p> |
|
715 * Since a {@link Message} body is read-once, sometimes |
|
716 * (such as when you do fail-over, or WS-RM) you need to |
|
717 * create an idential copy of a {@link Message}. |
|
718 * |
|
719 * <p> |
|
720 * The actual copy operation depends on the layout |
|
721 * of the data in memory, hence it's best to be done by |
|
722 * the {@link Message} implementation itself. |
|
723 * |
|
724 * <p> |
|
725 * The restrictions placed on the use of copied {@link Message} can be |
|
726 * relaxed if necessary, but it will make the copy method more expensive. |
|
727 */ |
|
728 // TODO: update the class javadoc with 'lifescope' |
|
729 // and move the discussion about life scope there. |
|
730 public abstract Message copy(); |
|
731 |
|
732 /** |
|
733 * Retuns a unique id for the message. The id can be used for various things, |
|
734 * like debug assistance, logging, and MIME encoding(say for boundary). |
|
735 * |
|
736 * <p> |
|
737 * This method will check the existence of the addressing <MessageID> header, |
|
738 * and if present uses that value. Otherwise it generates one from UUID.random(), |
|
739 * and return it without adding a new header. But it doesn't add a <MessageID> |
|
740 * to the header list since we expect them to be added before calling this |
|
741 * method. |
|
742 * |
|
743 * <p> |
|
744 * Addressing tube will go do a separate verification on inbound |
|
745 * headers to make sure that <MessageID> header is present when it's |
|
746 * supposed to be. |
|
747 * |
|
748 * @param binding object created by {@link BindingID#createBinding()} |
|
749 * |
|
750 * @return unique id for the message |
|
751 * @deprecated |
|
752 */ |
|
753 public @NotNull String getID(@NotNull WSBinding binding) { |
|
754 return getID(binding.getAddressingVersion(), binding.getSOAPVersion()); |
|
755 } |
|
756 |
|
757 /** |
|
758 * Retuns a unique id for the message. |
|
759 * <p><p> |
|
760 * @see {@link #getID(com.sun.xml.internal.ws.api.WSBinding)} for detailed description. |
|
761 * @param av WS-Addressing version |
|
762 * @param sv SOAP version |
|
763 * @return unique id for the message |
|
764 * @deprecated |
|
765 */ |
|
766 public @NotNull String getID(AddressingVersion av, SOAPVersion sv) { |
|
767 String uuid = null; |
|
768 if (av != null) { |
|
769 uuid = AddressingUtils.getMessageID(getHeaders(), av, sv); |
|
770 } |
|
771 if (uuid == null) { |
|
772 uuid = generateMessageID(); |
|
773 getHeaders().add(new StringHeader(av.messageIDTag, uuid)); |
|
774 } |
|
775 return uuid; |
|
776 } |
|
777 |
|
778 /** |
|
779 * Generates a UUID suitable for use as a MessageID value |
|
780 * @return generated UUID |
|
781 */ |
|
782 public static String generateMessageID() { |
|
783 return "uuid:" + UUID.randomUUID().toString(); |
|
784 } |
|
785 |
|
786 public SOAPVersion getSOAPVersion() { |
|
787 return null; |
|
788 } |
|
789 } |