1 /*
   2  * Copyright (c) 1997, 2014, 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     // 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      * <tt>Content-Type</tt> and <tt>Content-Length</tt>.
 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 <tt>""</tt>
 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             return ((MessageWritable)msg).getContentType();
1392         }
1393         return contentType;
1394     }
1395 
1396     public void setContentType(ContentType contentType) {
1397         this.contentType = contentType;
1398     }
1399 
1400     public enum Status {
1401         Request, Response, Unknown;
1402         public boolean isRequest()  { return Request.equals(this); }
1403         public boolean isResponse() { return Response.equals(this); }
1404     }
1405 
1406     public enum State {
1407         ServerRequest(true), ClientRequest(false), ServerResponse(false), ClientResponse(true);
1408         private boolean inbound;
1409         State(boolean inbound) {
1410             this.inbound = inbound;
1411         }
1412         public boolean isInbound() {
1413             return inbound;
1414         }
1415     }
1416 
1417 //    private Status status = Status.Unknown;
1418 
1419     //Default state is ServerRequest - some custom adapters may not set the value of state
1420     //upon server request - all other code paths should set it
1421     private State state = State.ServerRequest;
1422 
1423 //    public Status getStatus() { return status; }
1424 
1425     public State getState() { return state; }
1426     public void setState(State state) { this.state = state; }
1427 
1428     public boolean shouldUseMtom() {
1429         if (getState().isInbound()) {
1430             return isMtomContentType();
1431         } else {
1432             return shouldUseMtomOutbound();
1433         }
1434     }
1435 
1436     private boolean shouldUseMtomOutbound() {
1437         //Use the getter to make sure all the logic is executed correctly
1438         MTOMFeature myMtomFeature = getMtomFeature();
1439         if(myMtomFeature != null && myMtomFeature.isEnabled()) {
1440                 //If the content type is set already on this outbound Packet,
1441                 //(e.g.) through Codec.decode(InputStream, String contentType, Packet)
1442                 //and it is a non-mtom content type, then don't use mtom to encode it
1443                 ContentType curContentType = getInternalContentType();
1444                 if (curContentType != null && !isMtomContentType(curContentType)) {
1445                         return false;
1446                 }
1447             //On client, always use XOP encoding if MTOM is enabled
1448             //On Server, mtomAcceptable and mtomRequest will be set - use XOP encoding
1449             //if either request is XOP encoded (mtomRequest) or
1450             //client accepts XOP encoding (mtomAcceptable)
1451             if (getMtomAcceptable() == null && getMtomRequest() == null) {
1452                 return true;
1453             } else {
1454                 if (getMtomAcceptable() != null &&  getMtomAcceptable() && getState().equals(State.ServerResponse)) {
1455                     return true;
1456                 }
1457                 if (getMtomRequest() != null && getMtomRequest() && getState().equals(State.ServerResponse)) {
1458                     return true;
1459                 }
1460                 if (getMtomRequest() != null && getMtomRequest() && getState().equals(State.ClientRequest)) {
1461                     return true;
1462                 }
1463             }
1464         }
1465         return false;
1466     }
1467 
1468     private boolean isMtomContentType() {
1469         return (getInternalContentType() != null && isMtomContentType(getInternalContentType()));
1470     }
1471 
1472     private boolean isMtomContentType(ContentType cType) {
1473                 return cType.getContentType().contains("application/xop+xml");
1474         }
1475 
1476         /**
1477      * @deprecated
1478      */
1479     public void addSatellite(@NotNull com.sun.xml.internal.ws.api.PropertySet satellite) {
1480         super.addSatellite(satellite);
1481     }
1482 
1483     /**
1484      * @deprecated
1485      */
1486     public void addSatellite(@NotNull Class keyClass, @NotNull com.sun.xml.internal.ws.api.PropertySet satellite) {
1487         super.addSatellite(keyClass, satellite);
1488     }
1489 
1490     /**
1491      * @deprecated
1492      */
1493     public void copySatelliteInto(@NotNull com.sun.xml.internal.ws.api.DistributedPropertySet r) {
1494         super.copySatelliteInto(r);
1495     }
1496 
1497     /**
1498      * @deprecated
1499      */
1500     public void removeSatellite(com.sun.xml.internal.ws.api.PropertySet satellite) {
1501         super.removeSatellite(satellite);
1502     }
1503 
1504     /**
1505      * This is propogated from SOAPBindingCodec and will affect isMtomAcceptable and isFastInfosetAcceptable
1506      */
1507     private boolean isFastInfosetDisabled;
1508 
1509     public void setFastInfosetDisabled(boolean b) {
1510         isFastInfosetDisabled = b;
1511     }
1512 }