View Javadoc
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.oracle.webservices.internal.api.message.ContentType;
29  import com.oracle.webservices.internal.api.message.PropertySet;
30  import com.sun.istack.internal.NotNull;
31  import com.sun.istack.internal.Nullable;
32  import com.sun.xml.internal.bind.marshaller.SAX2DOMEx;
33  import com.sun.xml.internal.ws.addressing.WsaPropertyBag;
34  import com.sun.xml.internal.ws.addressing.WsaServerTube;
35  import com.sun.xml.internal.ws.addressing.WsaTubeHelper;
36  import com.sun.xml.internal.ws.api.Component;
37  import com.sun.xml.internal.ws.api.EndpointAddress;
38  import com.sun.xml.internal.ws.api.SOAPVersion;
39  import com.sun.xml.internal.ws.api.WSBinding;
40  import com.sun.xml.internal.ws.api.addressing.AddressingVersion;
41  import com.sun.xml.internal.ws.api.addressing.WSEndpointReference;
42  import com.sun.xml.internal.ws.api.model.JavaMethod;
43  import com.sun.xml.internal.ws.api.model.SEIModel;
44  import com.sun.xml.internal.ws.api.model.WSDLOperationMapping;
45  import com.sun.xml.internal.ws.api.model.wsdl.WSDLOperation;
46  import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
47  import com.sun.xml.internal.ws.api.pipe.Codec;
48  import com.sun.xml.internal.ws.api.pipe.Tube;
49  import com.sun.xml.internal.ws.api.server.Adapter;
50  import com.sun.xml.internal.ws.api.server.TransportBackChannel;
51  import com.sun.xml.internal.ws.api.server.WSEndpoint;
52  import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate;
53  import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory;
54  import com.sun.xml.internal.ws.client.*;
55  import com.sun.xml.internal.ws.developer.JAXWSProperties;
56  import com.sun.xml.internal.ws.encoding.MtomCodec;
57  import com.sun.xml.internal.ws.message.RelatesToHeader;
58  import com.sun.xml.internal.ws.message.StringHeader;
59  import com.sun.xml.internal.ws.util.DOMUtil;
60  import com.sun.xml.internal.ws.util.xml.XmlUtil;
61  import com.sun.xml.internal.ws.wsdl.DispatchException;
62  import com.sun.xml.internal.ws.wsdl.OperationDispatcher;
63  import com.sun.xml.internal.ws.resources.AddressingMessages;
64  
65  
66  import org.w3c.dom.Document;
67  import org.w3c.dom.Element;
68  import org.xml.sax.SAXException;
69  
70  import javax.xml.namespace.QName;
71  import javax.xml.soap.SOAPException;
72  import javax.xml.soap.SOAPMessage;
73  import javax.xml.stream.XMLStreamWriter;
74  import javax.xml.stream.XMLStreamException;
75  import javax.xml.ws.BindingProvider;
76  import javax.xml.ws.Dispatch;
77  import javax.xml.ws.WebServiceContext;
78  import javax.xml.ws.WebServiceException;
79  import javax.xml.ws.handler.LogicalMessageContext;
80  import javax.xml.ws.handler.MessageContext;
81  import javax.xml.ws.handler.soap.SOAPMessageContext;
82  import javax.xml.ws.soap.MTOMFeature;
83  
84  import java.util.*;
85  import java.util.logging.Logger;
86  import java.io.ByteArrayOutputStream;
87  import java.io.IOException;
88  import java.io.OutputStream;
89  import java.nio.channels.WritableByteChannel;
90  
91  /**
92   * Represents a container of a {@link Message}.
93   *
94   * <h2>What is a {@link Packet}?</h2>
95   * <p>
96   * A packet can be thought of as a frame/envelope/package that wraps
97   * a {@link Message}. A packet keeps track of optional metadata (properties)
98   * about a {@link Message} that doesn't go across the wire.
99   * This roughly corresponds to {@link MessageContext} in the JAX-WS API.
100  *
101  * <p>
102  * Usually a packet contains a {@link Message} in it, but sometimes
103  * (such as for a reply of an one-way operation), a packet may
104  * float around without a {@link Message} in it.
105  *
106  *
107  * <a name="properties"></a>
108  * <h2>Properties</h2>
109  * <p>
110  * Information frequently used inside the JAX-WS RI
111  * is stored in the strongly-typed fields. Other information is stored
112  * in terms of a generic {@link Map} (see
113  * {@link #invocationProperties}.)
114  *
115  * <p>
116  * Some properties need to be retained between request and response,
117  * some don't. For strongly typed fields, this characteristic is
118  * statically known for each of them, and propagation happens accordingly.
119  * For generic information stored in {@link Map}, {@link #invocationProperties}
120  * stores per-invocation scope information (which carries over to
121  * the response.)
122  *
123  * <p>
124  * This object is used as the backing store of {@link MessageContext}, and
125  * {@link LogicalMessageContext} and {@link SOAPMessageContext} will
126  * be delegating to this object for storing/retrieving values.
127  *
128  *
129  * <h3>Relationship to request/response context</h3>
130  * <p>
131  * {@link BindingProvider#getRequestContext() Request context} is used to
132  * seed the initial values of {@link Packet}.
133  * Some of those values go to strongly-typed fields, and others go to
134  * {@link #invocationProperties}, as they need to be retained in the reply message.
135  *
136  * <p>
137  * Similarly, {@link BindingProvider#getResponseContext() response context}
138  * is constructed from {@link Packet} (or rather it's just a view of {@link Packet}.)
139  * by using properties from {@link #invocationProperties},
140  * modulo properties named explicitly in {@link #getHandlerScopePropertyNames(boolean)}.
141  * IOW, properties added to {@link #invocationProperties}
142  * are exposed to the response context by default.
143  *
144  *
145  *
146  * <h3>TODO</h3>
147  * <ol>
148  *  <li>this class needs to be cloneable since Message is copiable.
149  *  <li>The three live views aren't implemented correctly. It will be
150  *      more work to do so, although I'm sure it's possible.
151  *  <li>{@link PropertySet.Property} annotation is to make it easy
152  *      for {@link MessageContext} to export properties on this object,
153  *      but it probably needs some clean up.
154  * </ol>
155  *
156  * @author Kohsuke Kawaguchi
157  */
158 public final class Packet
159         // Packet must continue to extend/implement deprecated interfaces until downstream
160         // usage is updated.
161     extends com.oracle.webservices.internal.api.message.BaseDistributedPropertySet
162     implements com.oracle.webservices.internal.api.message.MessageContext, MessageMetadata {
163 
164     /**
165      * Creates a {@link Packet} that wraps a given {@link Message}.
166      *
167      * <p>
168      * This method should be only used to create a fresh {@link Packet}.
169      * To create a {@link Packet} for a reply, use {@link #createResponse(Message)}.
170      *
171      * @param request
172      *      The request {@link Message}. Can be null.
173      */
174     public Packet(Message request) {
175         this();
176         this.message = request;
177         if (message != null) message.setMessageMedadata(this);
178     }
179 
180     /**
181      * Creates an empty {@link Packet} that doesn't have any {@link Message}.
182      */
183     public Packet() {
184         this.invocationProperties = new HashMap<String, Object>();
185     }
186 
187     /**
188      * Used by {@link #createResponse(Message)} and {@link #copy(boolean)}.
189      */
190     private Packet(Packet that) {
191         relatePackets(that, true);
192         this.invocationProperties = that.invocationProperties;
193     }
194 
195     /**
196      * Creates a copy of this {@link Packet}.
197      *
198      * @param copyMessage determines whether the {@link Message} from the original {@link Packet} should be copied as
199      *        well, or not. If the value is {@code false}, the {@link Message} in the copy of the {@link Packet} is {@code null}.
200      * @return copy of the original packet
201      */
202     public Packet copy(boolean copyMessage) {
203         // the copy constructor is originally designed for creating a response packet,
204         // but so far the implementation is usable for this purpose as well, so calling the copy constructor
205         // to avoid code dupliation.
206         Packet copy = new Packet(this);
207         if (copyMessage && this.message != null) {
208             copy.message = this.message.copy();
209         }
210         if (copy.message != null) copy.message.setMessageMedadata(copy);
211         return copy;
212     }
213 
214     private Message message;
215 
216     /**
217      * Gets the last {@link Message} set through {@link #setMessage(Message)}.
218      *
219      * @return may null. See the class javadoc for when it's null.
220      */
221     public Message getMessage() {
222         if (message != null && !(message instanceof MessageWrapper)) {
223             message = new MessageWrapper(this, message);
224         }
225         return  message;
226     }
227 
228     public Message getInternalMessage() {
229         return (message instanceof MessageWrapper)? ((MessageWrapper)message).delegate : message;
230     }
231 
232     public WSBinding getBinding() {
233         if (endpoint != null) {
234             return endpoint.getBinding();
235         }
236         if (proxy != null) {
237             return (WSBinding) proxy.getBinding();
238         }
239         return null;
240     }
241     /**
242      * Sets a {@link Message} to this packet.
243      *
244      * @param message Can be null.
245      */
246     public void setMessage(Message message) {
247         this.message = message;
248         if (message != null) this.message.setMessageMedadata(this);
249     }
250 
251     private WSDLOperationMapping wsdlOperationMapping = null;
252 
253     private QName wsdlOperation;
254 
255     /**
256      * Returns the QName of the wsdl operation associated with this packet.
257      * <p/>
258      * Information such as Payload QName, wsa:Action header, SOAPAction HTTP header are used depending on the features
259      * enabled on the particular port.
260      *
261      * @return null if there is no WSDL model or
262      *         runtime cannot uniquely identify the wsdl operation from the information in the packet.
263      */
264     @Property(MessageContext.WSDL_OPERATION)
265     public final
266     @Nullable
267     QName getWSDLOperation() {
268         if (wsdlOperation != null) return wsdlOperation;
269         if ( wsdlOperationMapping == null)  wsdlOperationMapping = getWSDLOperationMapping();
270         if ( wsdlOperationMapping != null ) wsdlOperation = wsdlOperationMapping.getOperationName();
271         return wsdlOperation;
272     }
273 
274     public WSDLOperationMapping getWSDLOperationMapping() {
275         if (wsdlOperationMapping != null) return wsdlOperationMapping;
276         OperationDispatcher opDispatcher = null;
277         if (endpoint != null) {
278             opDispatcher = endpoint.getOperationDispatcher();
279         } else if (proxy != null) {
280             opDispatcher = ((Stub) proxy).getOperationDispatcher();
281         }
282         //OpDispatcher is null when there is no WSDLModel
283         if (opDispatcher != null) {
284             try {
285                 wsdlOperationMapping = opDispatcher.getWSDLOperationMapping(this);
286             } catch (DispatchException e) {
287                 //Ignore, this might be a protocol message which may not have a wsdl operation
288                 //LOGGER.info("Cannot resolve wsdl operation that this Packet is targeted for.");
289             }
290         }
291         return wsdlOperationMapping;
292     }
293 
294     /**
295      * Set the wsdl operation to avoid lookup from other data.
296      * This is useful in SEI based clients, where the WSDL operation can be known
297      * from the associated {@link JavaMethod}
298      *
299      * @param wsdlOp QName
300      */
301     public void setWSDLOperation(QName wsdlOp) {
302         this.wsdlOperation = wsdlOp;
303     }
304 
305     /**
306      * True if this message came from a transport (IOW inbound),
307      * and in paricular from a "secure" transport. A transport
308      * needs to set this flag appropriately.
309      *
310      * <p>
311      * This is a requirement from the security team.
312      */
313     // TODO: expose this as a property
314     public boolean wasTransportSecure;
315 
316     /**
317      * Inbound transport headers are captured in a transport neutral way.
318      * Transports are expected to fill this data after creating a Packet.
319      * <p>
320      * {@link SOAPMessage#getMimeHeaders()} would return these headers.
321      */
322     public static final String INBOUND_TRANSPORT_HEADERS = "com.sun.xml.internal.ws.api.message.packet.inbound.transport.headers";
323 
324     /**
325      * Outbound transport headers are captured in a transport neutral way.
326      *
327      * <p>
328      * Transports may choose to ignore certain headers that interfere with
329      * its correct operation, such as
330      * <tt>Content-Type</tt> and <tt>Content-Length</tt>.
331      */
332     public static final String OUTBOUND_TRANSPORT_HEADERS = "com.sun.xml.internal.ws.api.message.packet.outbound.transport.headers";
333 
334     /**
335      *
336      */
337     public static final String HA_INFO = "com.sun.xml.internal.ws.api.message.packet.hainfo";
338 
339 
340     /**
341      * This property holds the snapshot of HandlerConfiguration
342      * at the time of invocation.
343      * This property is used by MUPipe and HandlerPipe implementations.
344      */
345     @Property(BindingProviderProperties.JAXWS_HANDLER_CONFIG)
346     public HandlerConfiguration handlerConfig;
347 
348     /**
349      * If a message originates from a proxy stub that implements
350      * a port interface, this field is set to point to that object.
351      *
352      * TODO: who's using this property?
353      */
354     @Property(BindingProviderProperties.JAXWS_CLIENT_HANDLE_PROPERTY)
355     public BindingProvider proxy;
356 
357     /**
358      * Determines if the governing {@link Adapter} or {@link com.sun.xml.internal.ws.api.pipe.Fiber.CompletionCallback}
359      * will handle delivering response messages targeted at non-anonymous endpoint
360      * addresses.  Prior to the introduction of this flag
361      * the {@link WsaServerTube} would deliver non-anonymous responses.
362      */
363     public boolean isAdapterDeliversNonAnonymousResponse;
364 
365     /**
366      * During invocation of a client Stub or Dispatch a Packet is
367      * created then the Stub's RequestContext is copied into the
368      * Packet.  On certain internal cases the Packet is created
369      * *before* the invocation.  In those cases we want the contents
370      * of the Packet to take precedence when ever any key/value pairs
371      * collide : if the Packet contains a value for a key use it,
372      * otherwise copy as usual from Stub.
373      */
374     public boolean packetTakesPriorityOverRequestContext = false;
375 
376     /**
377      * The endpoint address to which this message is sent to.
378      *
379      * <p>
380      * The JAX-WS spec allows this to be changed for each message,
381      * so it's designed to be a property.
382      *
383      * <p>
384      * Must not be null for a request message on the client. Otherwise
385      * it's null.
386      */
387     public EndpointAddress endpointAddress;
388 
389     /**
390      * @deprecated
391      *      The programatic acccess should be done via
392      *      {@link #endpointAddress}. This is for JAX-WS client applications
393      *      that access this property via {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}.
394      */
395     @Property(BindingProvider.ENDPOINT_ADDRESS_PROPERTY)
396     public String getEndPointAddressString() {
397         if (endpointAddress == null) {
398             return null;
399         } else {
400             return endpointAddress.toString();
401         }
402     }
403 
404     public void setEndPointAddressString(String s) {
405         if (s == null) {
406             this.endpointAddress = null;
407         } else {
408             this.endpointAddress = EndpointAddress.create(s);
409         }
410     }
411 
412     /**
413      * The value of {@link ContentNegotiation#PROPERTY}
414      * property.
415      * <p/>
416      * This property is used only on the client side.
417      */
418     public ContentNegotiation contentNegotiation;
419 
420     @Property(ContentNegotiation.PROPERTY)
421     public String getContentNegotiationString() {
422         return (contentNegotiation != null) ? contentNegotiation.toString() : null;
423     }
424 
425     public void setContentNegotiationString(String s) {
426         if (s == null) {
427             contentNegotiation = null;
428         } else {
429             try {
430                 contentNegotiation = ContentNegotiation.valueOf(s);
431             } catch (IllegalArgumentException e) {
432                 // If the value is not recognized default to none
433                 contentNegotiation = ContentNegotiation.none;
434             }
435         }
436     }
437 
438     /**
439      * Gives a list of Reference Parameters in the Message
440      * <p>
441      * Headers which have attribute wsa:IsReferenceParameter="true"
442      * This is not cached as one may reset the Message.
443      *<p>
444      */
445     @Property(MessageContext.REFERENCE_PARAMETERS)
446     public
447     @NotNull
448     List<Element> getReferenceParameters() {
449         Message msg = getMessage();
450         List<Element> refParams = new ArrayList<Element>();
451         if (msg == null) {
452             return refParams;
453         }
454         MessageHeaders hl = msg.getHeaders();
455         for (Header h : hl.asList()) {
456             String attr = h.getAttribute(AddressingVersion.W3C.nsUri, "IsReferenceParameter");
457             if (attr != null && (attr.equals("true") || attr.equals("1"))) {
458                 Document d = DOMUtil.createDom();
459                 SAX2DOMEx s2d = new SAX2DOMEx(d);
460                 try {
461                     h.writeTo(s2d, XmlUtil.DRACONIAN_ERROR_HANDLER);
462                     refParams.add((Element) d.getLastChild());
463                 } catch (SAXException e) {
464                     throw new WebServiceException(e);
465                 }
466                 /*
467                 DOMResult result = new DOMResult(d);
468                 XMLDOMWriterImpl domwriter = new XMLDOMWriterImpl(result);
469                 try {
470                     h.writeTo(domwriter);
471                     refParams.add((Element) result.getNode().getLastChild());
472                 } catch (XMLStreamException e) {
473                     throw new WebServiceException(e);
474                 }
475                 */
476             }
477         }
478         return refParams;
479     }
480 
481     /**
482      *      This method is for exposing header list through {@link PropertySet#get(Object)},
483      *      for user applications, and should never be invoked directly from within the JAX-WS RI.
484      */
485     @Property(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY)
486     /*package*/ MessageHeaders getHeaderList() {
487         Message msg = getMessage();
488         if (msg == null) {
489             return null;
490         }
491         return msg.getHeaders();
492     }
493 
494     /**
495      * The list of MIME types that are acceptable to a receiver
496      * of an outbound message.
497      *
498      * This property is used only on the server side.
499      *
500      * <p>The representation shall be that specified by the HTTP Accept
501      * request-header field.
502      *
503      * <p>The list of content types will be obtained from the transport
504      * meta-data of a inbound message in a request/response message exchange.
505      * Hence this property will be set by the service-side transport pipe.
506      */
507     public String acceptableMimeTypes;
508 
509     /**
510      * When non-null, this object is consulted to
511      * implement {@link WebServiceContext} methods
512      * exposed to the user application.
513      *
514      * Used only on the server side.
515      *
516      * <p>
517      * This property is set from the parameter
518      * of {@link WSEndpoint.PipeHead#process}.
519      */
520     public WebServiceContextDelegate webServiceContextDelegate;
521 
522     /**
523      * Used only on the server side so that the transport
524      * can close the connection early.
525      *
526      * <p>
527      * This field can be null. While a message is being processed,
528      * this field can be set explicitly to null, to prevent
529      * future pipes from closing a transport (see {@link #keepTransportBackChannelOpen()})
530      *
531      * <p>
532      * This property is set from the parameter
533      * of {@link WSEndpoint.PipeHead#process}.
534      */
535     public
536     @Nullable
537     TransportBackChannel transportBackChannel;
538 
539     /**
540      * Keeps the transport back channel open (by seeting {@link #transportBackChannel} to null.)
541      *
542      * @return
543      *      The previous value of {@link #transportBackChannel}.
544      */
545     public TransportBackChannel keepTransportBackChannelOpen() {
546         TransportBackChannel r = transportBackChannel;
547         transportBackChannel = null;
548         return r;
549     }
550 
551     /**
552       * The governing owner of this packet.  On the service-side this is the {@link Adapter} and on the client it is the {@link Stub}.
553       *
554       */
555      public Component component;
556 
557     /**
558      * The governing {@link WSEndpoint} in which this message is floating.
559      *
560      * <p>
561      * This property is set if and only if this is on the server side.
562      */
563     @Property(JAXWSProperties.WSENDPOINT)
564     public WSEndpoint endpoint;
565 
566     /**
567      * The value of the SOAPAction header associated with the message.
568      *
569      * <p>
570      * For outgoing messages, the transport may sends out this value.
571      * If this field is null, the transport may choose to send <tt>""</tt>
572      * (quoted empty string.)
573      *
574      * For incoming messages, the transport will set this field.
575      * If the incoming message did not contain the SOAPAction header,
576      * the transport sets this field to null.
577      *
578      * <p>
579      * If the value is non-null, it must be always in the quoted form.
580      * The value can be null.
581      *
582      * <p>
583      * Note that the way the transport sends this value out depends on
584      * transport and SOAP version.
585      * <p/>
586      * For HTTP transport and SOAP 1.1, BP requires that SOAPAction
587      * header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2,
588      * this is moved to the parameter of the "application/soap+xml".
589      */
590     @Property(BindingProvider.SOAPACTION_URI_PROPERTY)
591     public String soapAction;
592 
593     /**
594      * A hint indicating that whether a transport should expect
595      * a reply back from the server.
596      *
597      * <p>
598      * This property is used on the client-side for
599      * outbound messages, so that a pipeline
600      * can communicate to the terminal (or intermediate) {@link Tube}s
601      * about this knowledge.
602      *
603      * <p>
604      * This property <b>MUST NOT</b> be used by 2-way transports
605      * that have the transport back channel. Those transports
606      * must always check a reply coming through the transport back
607      * channel regardless of this value, and act accordingly.
608      * (This is because the expectation of the client and
609      * that of the server can be different, for example because
610      * of a bug in user's configuration.)
611      *
612      * <p>
613      * This property is for one-way transports, and more
614      * specifically for the coordinator that correlates sent requests
615      * and incoming replies, to decide whether to block
616      * until a response is received.
617      *
618      * <p>
619      * Also note that this property is related to
620      * {@link WSDLOperation#isOneWay()} but not the same thing.
621      * In fact in general, they are completely orthogonal.
622      *
623      * For example, the calling application can choose to invoke
624      * {@link Dispatch#invoke(Object)} or {@link Dispatch#invokeOneWay(Object)}
625      * with an operation (which determines the value of this property),
626      * regardless of whether WSDL actually says it's one way or not.
627      * So these two booleans can take any combinations.
628      *
629      *
630      * <p>
631      * When this property is {@link Boolean#FALSE}, it means that
632      * the pipeline does not expect a reply from a server (and therefore
633      * the correlator should not block for a reply message
634      * -- if such a reply does arrive, it can be just ignored.)
635      *
636      * <p>
637      * When this property is {@link Boolean#TRUE}, it means that
638      * the pipeline expects a reply from a server (and therefore
639      * the correlator should block to see if a reply message is received,
640      *
641      * <p>
642      * This property is always set to {@link Boolean#TRUE} or
643      * {@link Boolean#FALSE} when used on the request message
644      * on the client side.
645      * No other {@link Boolean} instances are allowed.
646      * <p>
647      *
648      * In all other situations, this property is null.
649      *
650      */
651     @Property(BindingProviderProperties.ONE_WAY_OPERATION)
652     public Boolean expectReply;
653 
654 
655     /**
656      * This property will be removed in a near future.
657      *
658      * <p>
659      * A part of what this flag represented moved to
660      * {@link #expectReply} and the other part was moved
661      * to {@link Message#isOneWay(WSDLPort)}. Please update
662      * your code soon, or risk breaking your build!!
663      */
664     @Deprecated
665     public Boolean isOneWay;
666 
667     /**
668      * Indicates whether is invoking a synchronous pattern. If true, no
669      * async client programming model (e.g. AsyncResponse or AsyncHandler)
670      * were used to make the request that created this packet.
671      */
672     public Boolean isSynchronousMEP;
673 
674     /**
675      * Indicates whether a non-null AsyncHandler was given at the point of
676      * making the request that created this packet. This flag can be used
677      * by Tube implementations to decide how to react when isSynchronousMEP
678      * is false. If true, the client gave a non-null AsyncHandler instance
679      * at the point of request, and will be expecting a response on that
680      * handler when this request has been processed.
681      */
682     public Boolean nonNullAsyncHandlerGiven;
683 
684     /**
685      * USE-CASE:
686      * WS-AT is enabled, but there is no WSDL available.
687      * If Packet.isRequestReplyMEP() is Boolean.TRUE then WS-AT should
688      * add the TX context.
689      *
690      * This value is exposed to users via facades at higher abstraction layers.
691      * The user should NEVER use Packet directly.
692      * This value should ONLY be set by users.
693      */
694     private Boolean isRequestReplyMEP;
695     public Boolean isRequestReplyMEP() { return isRequestReplyMEP; }
696     public void setRequestReplyMEP(final Boolean x) { isRequestReplyMEP = x; }
697 
698     /**
699      * Lazily created set of handler-scope property names.
700      *
701      * <p>
702      * We expect that this is only used when handlers are present
703      * and they explicitly set some handler-scope values.
704      *
705      * @see #getHandlerScopePropertyNames(boolean)
706      */
707     private Set<String> handlerScopePropertyNames;
708 
709     /**
710      * Bag to capture properties that are available for the whole
711      * message invocation (namely on both requests and responses.)
712      *
713      * <p>
714      * These properties are copied from a request to a response.
715      * This is where we keep properties that are set by handlers.
716      *
717      * <p>
718      * See <a href="#properties">class javadoc</a> for more discussion.
719      *
720      * @see #getHandlerScopePropertyNames(boolean)
721      */
722     public final Map<String, Object> invocationProperties;
723 
724     /**
725      * Gets a {@link Set} that stores handler-scope properties.
726      *
727      * <p>
728      * These properties will not be exposed to the response context.
729      * Consequently, if a {@link Tube} wishes to hide a property
730      * to {@link ResponseContext}, it needs to add the property name
731      * to this set.
732      *
733      * @param readOnly
734      *      Return true if the caller only intends to read the value of this set.
735      *      Internally, the {@link Set} is allocated lazily, and this flag helps
736      *      optimizing the strategy.
737      *
738      * @return
739      *      always non-null, possibly empty set that stores property names.
740      */
741     public final Set<String> getHandlerScopePropertyNames(boolean readOnly) {
742         Set<String> o = this.handlerScopePropertyNames;
743         if (o == null) {
744             if (readOnly) {
745                 return Collections.emptySet();
746             }
747             o = new HashSet<String>();
748             this.handlerScopePropertyNames = o;
749         }
750         return o;
751     }
752 
753     /**
754      * This method no longer works.
755      *
756      * @deprecated
757      *      Use {@link #getHandlerScopePropertyNames(boolean)}.
758      *      To be removed once Tango components are updated.
759      */
760     public final Set<String> getApplicationScopePropertyNames(boolean readOnly) {
761         assert false;
762         return new HashSet<String>();
763     }
764 
765     /**
766      * Creates a response {@link Packet} from a request packet ({@code this}).
767      *
768      * <p>
769      * When a {@link Packet} for a reply is created, some properties need to be
770      * copied over from a request to a response, and this method handles it correctly.
771      *
772      * @deprecated
773      *      Use createClientResponse(Message) for client side and
774      *      createServerResponse(Message, String) for server side response
775      *      creation.
776      *
777      * @param msg
778      *      The {@link Message} that represents a reply. Can be null.
779      */
780     @Deprecated
781     public Packet createResponse(Message msg) {
782         Packet response = new Packet(this);
783         response.setMessage(msg);
784         return response;
785     }
786 
787     /**
788      * Creates a response {@link Packet} from a request packet ({@code this}).
789      *
790      * <p>
791      * When a {@link Packet} for a reply is created, some properties need to be
792      * copied over from a request to a response, and this method handles it correctly.
793      *
794      * @param msg
795      *      The {@link Message} that represents a reply. Can be null.
796      */
797     public Packet createClientResponse(Message msg) {
798         Packet response = new Packet(this);
799         response.setMessage(msg);
800         finishCreateRelateClientResponse(response);
801         return response;
802     }
803 
804     /**
805      * For use cases that start with an existing Packet.
806      */
807     public Packet relateClientResponse(final Packet response) {
808         response.relatePackets(this, true);
809         finishCreateRelateClientResponse(response);
810         return response;
811     }
812 
813     private void finishCreateRelateClientResponse(final Packet response) {
814         response.soapAction = null; // de-initializing
815         response.setState(State.ClientResponse);
816     }
817 
818     /**
819      * Creates a server-side response {@link Packet} from a request
820      * packet ({@code this}). If WS-Addressing is enabled, a default Action
821      * Message Addressing Property is obtained using <code>wsdlPort</code> {@link WSDLPort}
822      * and <code>binding</code> {@link WSBinding}.
823      * <p><p>
824      * This method should be called to create application response messages
825      * since they are associated with a {@link WSBinding} and {@link WSDLPort}.
826      * For creating protocol messages that require a non-default Action, use
827      * {@link #createServerResponse(Message, com.sun.xml.internal.ws.api.addressing.AddressingVersion, com.sun.xml.internal.ws.api.SOAPVersion, String)}.
828      *
829      * @param responseMessage The {@link Message} that represents a reply. Can be null.
830      * @param wsdlPort The response WSDL port.
831      * @param binding The response Binding. Cannot be null.
832      * @return response packet
833      */
834     public Packet createServerResponse(@Nullable Message responseMessage, @Nullable WSDLPort wsdlPort, @Nullable SEIModel seiModel, @NotNull WSBinding binding) {
835         Packet r = createClientResponse(responseMessage);
836         return relateServerResponse(r, wsdlPort, seiModel, binding);
837     }
838 
839     /**
840      * Copy all properties from ({@code this}) packet into a input {@link Packet}
841      * @param response packet
842      */
843     public void copyPropertiesTo(@Nullable Packet response){
844         relatePackets(response, false);
845     }
846 
847 
848     /**
849      * A common method to make members related between input packet and this packet
850      *
851      * @param packet
852      * @param isCopy 'true' means copying all properties from input packet;
853      *               'false' means copying all properties from this packet to input packet.
854      */
855     private void relatePackets(@Nullable Packet packet, boolean isCopy)
856     {
857         Packet request;
858             Packet response;
859 
860         if (!isCopy) { //is relate
861           request = this;
862           response = packet;
863 
864           // processing specific properties
865           response.soapAction = null;
866           response.invocationProperties.putAll(request.invocationProperties);
867           if (this.getState().equals(State.ServerRequest)) {
868               response.setState(State.ServerResponse);
869           }
870         } else { //is copy constructor
871           request = packet;
872           response = this;
873 
874           // processing specific properties
875           response.soapAction = request.soapAction;
876           response.setState(request.getState());
877         }
878 
879         request.copySatelliteInto(response);
880         response.isAdapterDeliversNonAnonymousResponse = request.isAdapterDeliversNonAnonymousResponse;
881         response.handlerConfig = request.handlerConfig;
882         response.handlerScopePropertyNames = request.handlerScopePropertyNames;
883         response.contentNegotiation = request.contentNegotiation;
884         response.wasTransportSecure = request.wasTransportSecure;
885         response.transportBackChannel = request.transportBackChannel;
886         response.endpointAddress = request.endpointAddress;
887         response.wsdlOperation = request.wsdlOperation;
888         response.wsdlOperationMapping = request.wsdlOperationMapping;
889         response.acceptableMimeTypes = request.acceptableMimeTypes;
890         response.endpoint = request.endpoint;
891         response.proxy = request.proxy;
892         response.webServiceContextDelegate = request.webServiceContextDelegate;
893         response.expectReply = request.expectReply;
894         response.component = request.component;
895         response.mtomAcceptable = request.mtomAcceptable;
896         response.mtomRequest = request.mtomRequest;
897         // copy other properties that need to be copied. is there any?
898     }
899 
900 
901     public Packet relateServerResponse(@Nullable Packet r, @Nullable WSDLPort wsdlPort, @Nullable SEIModel seiModel, @NotNull WSBinding binding) {
902         relatePackets(r, false);
903         r.setState(State.ServerResponse);
904         AddressingVersion av = binding.getAddressingVersion();
905         // populate WS-A headers only if WS-A is enabled
906         if (av == null) {
907             return r;
908         }
909 
910         if (getMessage() == null) {
911             return r;
912         }
913 
914         //populate WS-A headers only if the request has addressing headers
915         String inputAction = AddressingUtils.getAction(getMessage().getHeaders(), av, binding.getSOAPVersion());
916         if (inputAction == null) {
917             return r;
918         }
919         // if one-way, then dont populate any WS-A headers
920         if (r.getMessage() == null || (wsdlPort != null && getMessage().isOneWay(wsdlPort))) {
921             return r;
922         }
923 
924         // otherwise populate WS-Addressing headers
925         populateAddressingHeaders(binding, r, wsdlPort, seiModel);
926         return r;
927     }
928 
929     /**
930      * Creates a server-side response {@link Packet} from a request
931      * packet ({@code this}). If WS-Addressing is enabled, <code>action</code>
932      * is used as Action Message Addressing Property.
933      * <p><p>
934      * This method should be called only for creating protocol response messages
935      * that require a particular value of Action since they are not associated
936      * with a {@link WSBinding} and {@link WSDLPort} but do know the {@link AddressingVersion}
937      * and {@link SOAPVersion}.
938      *
939      * @param responseMessage The {@link Message} that represents a reply. Can be null.
940      * @param addressingVersion The WS-Addressing version of the response message.
941      * @param soapVersion The SOAP version of the response message.
942      * @param action The response Action Message Addressing Property value.
943      * @return response packet
944      */
945     public Packet createServerResponse(@Nullable Message responseMessage, @NotNull AddressingVersion addressingVersion, @NotNull SOAPVersion soapVersion, @NotNull String action) {
946         Packet responsePacket = createClientResponse(responseMessage);
947         responsePacket.setState(State.ServerResponse);
948         // populate WS-A headers only if WS-A is enabled
949         if (addressingVersion == null) {
950             return responsePacket;
951         }
952         //populate WS-A headers only if the request has addressing headers
953         String inputAction = AddressingUtils.getAction(this.getMessage().getHeaders(), addressingVersion, soapVersion);
954         if (inputAction == null) {
955             return responsePacket;
956         }
957 
958         populateAddressingHeaders(responsePacket, addressingVersion, soapVersion, action, false);
959         return responsePacket;
960     }
961 
962     /**
963      * Overwrites the {@link Message} of the response packet ({@code this}) by the given {@link Message}.
964      * Unlike {@link #setMessage(Message)}, fill in the addressing headers correctly, and this process
965      * requires the access to the request packet.
966      *
967      * <p>
968      * This method is useful when the caller needs to swap a response message completely to a new one.
969      *
970      * @see #createServerResponse(Message, AddressingVersion, SOAPVersion, String)
971      */
972     public void setResponseMessage(@NotNull Packet request, @Nullable Message responseMessage, @NotNull AddressingVersion addressingVersion, @NotNull SOAPVersion soapVersion, @NotNull String action) {
973         Packet temp = request.createServerResponse(responseMessage, addressingVersion, soapVersion, action);
974         setMessage(temp.getMessage());
975     }
976 
977     private void populateAddressingHeaders(Packet responsePacket, AddressingVersion av, SOAPVersion sv, String action, boolean mustUnderstand) {
978         // populate WS-A headers only if WS-A is enabled
979         if (av == null) return;
980 
981         // if one-way, then dont populate any WS-A headers
982         if (responsePacket.getMessage() == null)
983             return;
984 
985         MessageHeaders hl = responsePacket.getMessage().getHeaders();
986 
987         WsaPropertyBag wpb = getSatellite(WsaPropertyBag.class);
988         Message msg = getMessage();
989         // wsa:To
990         WSEndpointReference replyTo = null;
991         Header replyToFromRequestMsg = AddressingUtils.getFirstHeader(msg.getHeaders(), av.replyToTag, true, sv);
992         Header replyToFromResponseMsg = hl.get(av.toTag, false);
993         boolean replaceToTag = true;
994         try{
995             if (replyToFromRequestMsg != null){
996                 replyTo = replyToFromRequestMsg.readAsEPR(av);
997             }
998             if (replyToFromResponseMsg != null && replyTo == null) {
999                 replaceToTag = false;
1000             }
1001         } catch (XMLStreamException e) {
1002             throw new WebServiceException(AddressingMessages.REPLY_TO_CANNOT_PARSE(), e);
1003         }
1004         if (replyTo == null) {
1005               replyTo = AddressingUtils.getReplyTo(msg.getHeaders(), av, sv);
1006         }
1007 
1008         // wsa:Action, add if the message doesn't already contain it,
1009         // generally true for SEI case where there is SEIModel or WSDLModel
1010         //           false for Provider with no wsdl, Expects User to set the coresponding header on the Message.
1011         if (AddressingUtils.getAction(responsePacket.getMessage().getHeaders(), av, sv) == null) {
1012             //wsa:Action header is not set in the message, so use the wsa:Action  passed as the parameter.
1013             hl.add(new StringHeader(av.actionTag, action, sv, mustUnderstand));
1014         }
1015 
1016         // wsa:MessageID
1017         if (responsePacket.getMessage().getHeaders().get(av.messageIDTag, false) == null) {
1018             // if header doesn't exist, method getID creates a new random id
1019             String newID = Message.generateMessageID();
1020             hl.add(new StringHeader(av.messageIDTag, newID));
1021         }
1022 
1023         // wsa:RelatesTo
1024         String mid = null;
1025         if (wpb != null) {
1026             mid = wpb.getMessageID();
1027         }
1028         if (mid == null) {
1029             mid = AddressingUtils.getMessageID(msg.getHeaders(), av, sv);
1030         }
1031         if (mid != null) {
1032             hl.addOrReplace(new RelatesToHeader(av.relatesToTag, mid));
1033         }
1034 
1035 
1036         // populate reference parameters
1037         WSEndpointReference refpEPR = null;
1038         if (responsePacket.getMessage().isFault()) {
1039             // choose FaultTo
1040             if (wpb != null) {
1041                 refpEPR = wpb.getFaultToFromRequest();
1042             }
1043             if (refpEPR == null) {
1044                 refpEPR = AddressingUtils.getFaultTo(msg.getHeaders(), av, sv);
1045             }
1046             // if FaultTo is null, then use ReplyTo
1047             if (refpEPR == null) {
1048                 refpEPR = replyTo;
1049             }
1050         } else {
1051             // choose ReplyTo
1052             refpEPR = replyTo;
1053         }
1054         if (replaceToTag && refpEPR != null) {
1055             hl.addOrReplace(new StringHeader(av.toTag, refpEPR.getAddress()));
1056             refpEPR.addReferenceParametersToList(hl);
1057         }
1058     }
1059 
1060     private void populateAddressingHeaders(WSBinding binding, Packet responsePacket, WSDLPort wsdlPort, SEIModel seiModel) {
1061         AddressingVersion addressingVersion = binding.getAddressingVersion();
1062 
1063         if (addressingVersion == null) {
1064             return;
1065         }
1066 
1067         WsaTubeHelper wsaHelper = addressingVersion.getWsaHelper(wsdlPort, seiModel, binding);
1068         String action = responsePacket.getMessage().isFault() ?
1069                 wsaHelper.getFaultAction(this, responsePacket) :
1070                 wsaHelper.getOutputAction(this);
1071         if (action == null) {
1072             LOGGER.info("WSA headers are not added as value for wsa:Action cannot be resolved for this message");
1073             return;
1074         }
1075         populateAddressingHeaders(responsePacket, addressingVersion, binding.getSOAPVersion(), action, AddressingVersion.isRequired(binding));
1076     }
1077 
1078     public String toShortString() {
1079       return super.toString();
1080     }
1081 
1082     // For use only in a debugger
1083     @Override
1084     public String toString() {
1085       StringBuilder buf = new StringBuilder();
1086       buf.append(super.toString());
1087       String content;
1088         try {
1089             Message msg = getMessage();
1090         if (msg != null) {
1091                         ByteArrayOutputStream baos = new ByteArrayOutputStream();
1092                         XMLStreamWriter xmlWriter = XMLStreamWriterFactory.create(baos, "UTF-8");
1093                         msg.copy().writeTo(xmlWriter);
1094                         xmlWriter.flush();
1095                         xmlWriter.close();
1096                         baos.flush();
1097                         XMLStreamWriterFactory.recycle(xmlWriter);
1098 
1099                         byte[] bytes = baos.toByteArray();
1100                         //message = Messages.create(XMLStreamReaderFactory.create(null, new ByteArrayInputStream(bytes), "UTF-8", true));
1101                         content = new String(bytes, "UTF-8");
1102                 } else {
1103                     content = "<none>";
1104         }
1105         } catch (Throwable t) {
1106                 throw new WebServiceException(t);
1107         }
1108       buf.append(" Content: ").append(content);
1109       return buf.toString();
1110     }
1111 
1112     // completes TypedMap
1113     private static final PropertyMap model;
1114 
1115     static {
1116         model = parse(Packet.class);
1117     }
1118 
1119     @Override
1120     protected PropertyMap getPropertyMap() {
1121         return model;
1122     }
1123 
1124     public Map<String, Object> asMapIncludingInvocationProperties() {
1125         final Map<String, Object> asMap = asMap();
1126         return new AbstractMap<String, Object>() {
1127             @Override
1128             public Object get(Object key) {
1129                 Object o = asMap.get(key);
1130                 if (o != null)
1131                     return o;
1132 
1133                 return invocationProperties.get(key);
1134             }
1135 
1136             @Override
1137             public int size() {
1138                 return asMap.size() + invocationProperties.size();
1139             }
1140 
1141             @Override
1142             public boolean containsKey(Object key) {
1143                 if (asMap.containsKey(key))
1144                     return true;
1145                 return invocationProperties.containsKey(key);
1146             }
1147 
1148             @Override
1149             public Set<Entry<String, Object>> entrySet() {
1150                 final Set<Entry<String, Object>> asMapEntries = asMap.entrySet();
1151                 final Set<Entry<String, Object>> ipEntries = invocationProperties.entrySet();
1152 
1153                 return new AbstractSet<Entry<String, Object>>() {
1154                     @Override
1155                     public Iterator<Entry<String, Object>> iterator() {
1156                         final Iterator<Entry<String, Object>> asMapIt = asMapEntries.iterator();
1157                         final Iterator<Entry<String, Object>> ipIt = ipEntries.iterator();
1158 
1159                         return new Iterator<Entry<String, Object>>() {
1160                             @Override
1161                             public boolean hasNext() {
1162                                 return asMapIt.hasNext() || ipIt.hasNext();
1163                             }
1164 
1165                             @Override
1166                             public java.util.Map.Entry<String, Object> next() {
1167                                 if (asMapIt.hasNext())
1168                                     return asMapIt.next();
1169                                 return ipIt.next();
1170                             }
1171 
1172                             @Override
1173                             public void remove() {
1174                                 throw new UnsupportedOperationException();
1175                             }
1176                         };
1177                     }
1178 
1179                     @Override
1180                     public int size() {
1181                         return asMap.size() + invocationProperties.size();
1182                     }
1183                 };
1184             }
1185 
1186             @Override
1187             public Object put(String key, Object value) {
1188                 if (supports(key))
1189                     return asMap.put(key, value);
1190 
1191                 return invocationProperties.put(key, value);
1192             }
1193 
1194             @Override
1195             public void clear() {
1196                 asMap.clear();
1197                 invocationProperties.clear();
1198             }
1199 
1200             @Override
1201             public Object remove(Object key) {
1202                 if (supports(key))
1203                     return asMap.remove(key);
1204 
1205                 return invocationProperties.remove(key);
1206             }
1207         };
1208     }
1209 
1210     private static final Logger LOGGER = Logger.getLogger(Packet.class.getName());
1211 
1212     @Override
1213     public SOAPMessage getSOAPMessage() throws SOAPException {
1214         return getAsSOAPMessage();
1215     }
1216 
1217     //TODO replace the message to a SAAJMEssage issue - JRFSAAJMessage or SAAJMessage?
1218     @Override
1219     public SOAPMessage getAsSOAPMessage() throws SOAPException {
1220         Message msg = this.getMessage();
1221         if (msg == null)
1222             return null;
1223         if (msg instanceof MessageWritable)
1224             ((MessageWritable) msg).setMTOMConfiguration(mtomFeature);
1225         return msg.readAsSOAPMessage(this, this.getState().isInbound());
1226     }
1227 
1228     public
1229     Codec codec = null;
1230     public Codec getCodec() {
1231         if (codec != null) {
1232             return codec;
1233         }
1234         if (endpoint != null) {
1235             codec = endpoint.createCodec();
1236         }
1237         WSBinding wsb = getBinding();
1238         if (wsb != null) {
1239             codec = wsb.getBindingId().createEncoder(wsb);
1240         }
1241         return codec;
1242     }
1243 
1244     @Override
1245     public com.oracle.webservices.internal.api.message.ContentType writeTo( OutputStream out ) throws IOException {
1246         Message msg = getInternalMessage();
1247         if (msg instanceof MessageWritable) {
1248             ((MessageWritable) msg).setMTOMConfiguration(mtomFeature);
1249             return ((MessageWritable)msg).writeTo(out);
1250         }
1251         return getCodec().encode(this, out);
1252     }
1253 
1254     public com.oracle.webservices.internal.api.message.ContentType writeTo( WritableByteChannel buffer ) {
1255         return getCodec().encode(this, buffer);
1256     }
1257 
1258     private ContentType contentType;
1259 
1260     /**
1261      * If the request's Content-Type is multipart/related; type=application/xop+xml, then this set to to true
1262      *
1263      * Used on server-side, for encoding the repsonse.
1264      */
1265     private Boolean mtomRequest;
1266 
1267     /**
1268      * Based on request's Accept header this is set.
1269      * Currently only set if MTOMFeature is enabled.
1270      *
1271      * Should be used on server-side, for encoding the response.
1272      */
1273     private Boolean mtomAcceptable;
1274 
1275     private MTOMFeature mtomFeature;
1276 
1277     public Boolean getMtomRequest() {
1278         return mtomRequest;
1279     }
1280 
1281     public void setMtomRequest(Boolean mtomRequest) {
1282         this.mtomRequest = mtomRequest;
1283     }
1284 
1285     public Boolean getMtomAcceptable() {
1286         return mtomAcceptable;
1287     }
1288 
1289     Boolean checkMtomAcceptable;
1290     public void checkMtomAcceptable() {
1291         if (checkMtomAcceptable == null) {
1292             if (acceptableMimeTypes == null || isFastInfosetDisabled) {
1293                 checkMtomAcceptable = false;
1294             } else {
1295                 checkMtomAcceptable = (acceptableMimeTypes.indexOf(MtomCodec.XOP_XML_MIME_TYPE) != -1);
1296 //                StringTokenizer st = new StringTokenizer(acceptableMimeTypes, ",");
1297 //                while (st.hasMoreTokens()) {
1298 //                    final String token = st.nextToken().trim();
1299 //                    if (token.toLowerCase().contains(MtomCodec.XOP_XML_MIME_TYPE)) {
1300 //                        mtomAcceptable = true;
1301 //                    }
1302 //                }
1303 //                if (mtomAcceptable == null) mtomAcceptable = false;
1304             }
1305         }
1306         mtomAcceptable = checkMtomAcceptable;
1307     }
1308 
1309     private Boolean fastInfosetAcceptable;
1310 
1311     public Boolean getFastInfosetAcceptable(String fiMimeType) {
1312         if (fastInfosetAcceptable == null) {
1313             if (acceptableMimeTypes == null || isFastInfosetDisabled) {
1314                 fastInfosetAcceptable = false;
1315             } else {
1316                 fastInfosetAcceptable = (acceptableMimeTypes.indexOf(fiMimeType) != -1);
1317             }
1318 //        if (accept == null || isFastInfosetDisabled) return false;
1319 //
1320 //        StringTokenizer st = new StringTokenizer(accept, ",");
1321 //        while (st.hasMoreTokens()) {
1322 //            final String token = st.nextToken().trim();
1323 //            if (token.equalsIgnoreCase(fiMimeType)) {
1324 //                return true;
1325 //            }
1326 //        }
1327 //        return false;
1328         }
1329         return fastInfosetAcceptable;
1330     }
1331 
1332 
1333     public void setMtomFeature(MTOMFeature mtomFeature) {
1334         this.mtomFeature = mtomFeature;
1335     }
1336 
1337     public MTOMFeature getMtomFeature() {
1338         //If we have a binding, use that in preference to an explicitly
1339         //set MTOMFeature
1340         WSBinding binding = getBinding();
1341         if (binding != null) {
1342             return binding.getFeature(MTOMFeature.class);
1343         }
1344         return mtomFeature;
1345     }
1346 
1347     @Override
1348     public com.oracle.webservices.internal.api.message.ContentType getContentType() {
1349         if (contentType == null) {
1350             contentType = getInternalContentType();
1351         }
1352         if (contentType == null) {
1353             contentType = getCodec().getStaticContentType(this);
1354         }
1355         if (contentType == null) {
1356             //TODO write to buffer
1357         }
1358         return contentType;
1359     }
1360 
1361     public ContentType getInternalContentType() {
1362         Message msg = getInternalMessage();
1363         if (msg instanceof MessageWritable) {
1364             return ((MessageWritable)msg).getContentType();
1365         }
1366         return contentType;
1367     }
1368 
1369     public void setContentType(ContentType contentType) {
1370         this.contentType = contentType;
1371     }
1372 
1373     public enum Status {
1374         Request, Response, Unknown;
1375         public boolean isRequest()  { return Request.equals(this); }
1376         public boolean isResponse() { return Response.equals(this); }
1377     }
1378 
1379     public enum State {
1380         ServerRequest(true), ClientRequest(false), ServerResponse(false), ClientResponse(true);
1381         private boolean inbound;
1382         State(boolean inbound) {
1383             this.inbound = inbound;
1384         }
1385         public boolean isInbound() {
1386             return inbound;
1387         }
1388     }
1389 
1390 //    private Status status = Status.Unknown;
1391 
1392     //Default state is ServerRequest - some custom adapters may not set the value of state
1393     //upon server request - all other code paths should set it
1394     private State state = State.ServerRequest;
1395 
1396 //    public Status getStatus() { return status; }
1397 
1398     public State getState() { return state; }
1399     public void setState(State state) { this.state = state; }
1400 
1401     public boolean shouldUseMtom() {
1402         if (getState().isInbound()) {
1403             return isMtomContentType();
1404         } else {
1405             return shouldUseMtomOutbound();
1406         }
1407     }
1408 
1409     private boolean shouldUseMtomOutbound() {
1410         //Use the getter to make sure all the logic is executed correctly
1411         MTOMFeature myMtomFeature = getMtomFeature();
1412         if(myMtomFeature != null && myMtomFeature.isEnabled()) {
1413             //On client, always use XOP encoding if MTOM is enabled
1414             //On Server, mtomAcceptable and mtomRequest will be set - use XOP encoding
1415             //if either request is XOP encoded (mtomRequest) or
1416             //client accepts XOP encoding (mtomAcceptable)
1417             if (getMtomAcceptable() == null && getMtomRequest() == null) {
1418                 return true;
1419             } else {
1420                 if (getMtomAcceptable() != null &&  getMtomAcceptable() && getState().equals(State.ServerResponse)) {
1421                     return true;
1422                 }
1423                 if (getMtomRequest() != null && getMtomRequest() && getState().equals(State.ServerResponse)) {
1424                     return true;
1425                 }
1426                 if (getMtomRequest() != null && getMtomRequest() && getState().equals(State.ClientRequest)) {
1427                     return true;
1428                 }
1429             }
1430         }
1431         return false;
1432     }
1433 
1434     private boolean isMtomContentType() {
1435         return (getInternalContentType() != null) &&
1436         (getInternalContentType().getContentType().contains("application/xop+xml"));
1437     }
1438 
1439     /**
1440      * @deprecated
1441      */
1442     public void addSatellite(@NotNull com.sun.xml.internal.ws.api.PropertySet satellite) {
1443         super.addSatellite(satellite);
1444     }
1445 
1446     /**
1447      * @deprecated
1448      */
1449     public void addSatellite(@NotNull Class keyClass, @NotNull com.sun.xml.internal.ws.api.PropertySet satellite) {
1450         super.addSatellite(keyClass, satellite);
1451     }
1452 
1453     /**
1454      * @deprecated
1455      */
1456     public void copySatelliteInto(@NotNull com.sun.xml.internal.ws.api.DistributedPropertySet r) {
1457         super.copySatelliteInto(r);
1458     }
1459 
1460     /**
1461      * @deprecated
1462      */
1463     public void removeSatellite(com.sun.xml.internal.ws.api.PropertySet satellite) {
1464         super.removeSatellite(satellite);
1465     }
1466 
1467     /**
1468      * This is propogated from SOAPBindingCodec and will affect isMtomAcceptable and isFastInfosetAcceptable
1469      */
1470     private boolean isFastInfosetDisabled;
1471 
1472     public void setFastInfosetDisabled(boolean b) {
1473         isFastInfosetDisabled = b;
1474     }
1475 }