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