/* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.xml.internal.ws.api.message; import com.oracle.webservices.internal.api.message.ContentType; import com.oracle.webservices.internal.api.message.PropertySet; import com.sun.istack.internal.NotNull; import com.sun.istack.internal.Nullable; import com.sun.xml.internal.bind.marshaller.SAX2DOMEx; import com.sun.xml.internal.ws.addressing.WsaPropertyBag; import com.sun.xml.internal.ws.addressing.WsaServerTube; import com.sun.xml.internal.ws.addressing.WsaTubeHelper; import com.sun.xml.internal.ws.api.Component; import com.sun.xml.internal.ws.api.EndpointAddress; import com.sun.xml.internal.ws.api.SOAPVersion; import com.sun.xml.internal.ws.api.WSBinding; import com.sun.xml.internal.ws.api.addressing.AddressingVersion; import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; import com.sun.xml.internal.ws.api.model.JavaMethod; import com.sun.xml.internal.ws.api.model.SEIModel; import com.sun.xml.internal.ws.api.model.WSDLOperationMapping; import com.sun.xml.internal.ws.api.model.wsdl.WSDLOperation; import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; import com.sun.xml.internal.ws.api.pipe.Codec; import com.sun.xml.internal.ws.api.pipe.Tube; import com.sun.xml.internal.ws.api.server.Adapter; import com.sun.xml.internal.ws.api.server.TransportBackChannel; import com.sun.xml.internal.ws.api.server.WSEndpoint; import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate; import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory; import com.sun.xml.internal.ws.client.*; import com.sun.xml.internal.ws.developer.JAXWSProperties; import com.sun.xml.internal.ws.encoding.MtomCodec; import com.sun.xml.internal.ws.message.RelatesToHeader; import com.sun.xml.internal.ws.message.StringHeader; import com.sun.xml.internal.ws.util.DOMUtil; import com.sun.xml.internal.ws.util.xml.XmlUtil; import com.sun.xml.internal.ws.wsdl.DispatchException; import com.sun.xml.internal.ws.wsdl.OperationDispatcher; import com.sun.xml.internal.ws.resources.AddressingMessages; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; import javax.xml.namespace.QName; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; import javax.xml.stream.XMLStreamWriter; import javax.xml.stream.XMLStreamException; import javax.xml.ws.BindingProvider; import javax.xml.ws.Dispatch; import javax.xml.ws.WebServiceContext; import javax.xml.ws.WebServiceException; import javax.xml.ws.handler.LogicalMessageContext; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPMessageContext; import javax.xml.ws.soap.MTOMFeature; import java.util.*; import java.util.logging.Logger; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.channels.WritableByteChannel; /** * Represents a container of a {@link Message}. * *

What is a {@link Packet}?

*

* A packet can be thought of as a frame/envelope/package that wraps * a {@link Message}. A packet keeps track of optional metadata (properties) * about a {@link Message} that doesn't go across the wire. * This roughly corresponds to {@link MessageContext} in the JAX-WS API. * *

* Usually a packet contains a {@link Message} in it, but sometimes * (such as for a reply of an one-way operation), a packet may * float around without a {@link Message} in it. * * * *

Properties

*

* Information frequently used inside the JAX-WS RI * is stored in the strongly-typed fields. Other information is stored * in terms of a generic {@link Map} (see * {@link #invocationProperties}.) * *

* Some properties need to be retained between request and response, * some don't. For strongly typed fields, this characteristic is * statically known for each of them, and propagation happens accordingly. * For generic information stored in {@link Map}, {@link #invocationProperties} * stores per-invocation scope information (which carries over to * the response.) * *

* This object is used as the backing store of {@link MessageContext}, and * {@link LogicalMessageContext} and {@link SOAPMessageContext} will * be delegating to this object for storing/retrieving values. * * *

Relationship to request/response context

*

* {@link BindingProvider#getRequestContext() Request context} is used to * seed the initial values of {@link Packet}. * Some of those values go to strongly-typed fields, and others go to * {@link #invocationProperties}, as they need to be retained in the reply message. * *

* Similarly, {@link BindingProvider#getResponseContext() response context} * is constructed from {@link Packet} (or rather it's just a view of {@link Packet}.) * by using properties from {@link #invocationProperties}, * modulo properties named explicitly in {@link #getHandlerScopePropertyNames(boolean)}. * IOW, properties added to {@link #invocationProperties} * are exposed to the response context by default. * * * *

TODO

*
    *
  1. this class needs to be cloneable since Message is copiable. *
  2. The three live views aren't implemented correctly. It will be * more work to do so, although I'm sure it's possible. *
  3. {@link PropertySet.Property} annotation is to make it easy * for {@link MessageContext} to export properties on this object, * but it probably needs some clean up. *
* * @author Kohsuke Kawaguchi */ public final class Packet // Packet must continue to extend/implement deprecated interfaces until downstream // usage is updated. extends com.oracle.webservices.internal.api.message.BaseDistributedPropertySet implements com.oracle.webservices.internal.api.message.MessageContext, MessageMetadata { /** * Creates a {@link Packet} that wraps a given {@link Message}. * *

* This method should be only used to create a fresh {@link Packet}. * To create a {@link Packet} for a reply, use {@link #createResponse(Message)}. * * @param request * The request {@link Message}. Can be null. */ public Packet(Message request) { this(); this.message = request; if (message != null) message.setMessageMedadata(this); } /** * Creates an empty {@link Packet} that doesn't have any {@link Message}. */ public Packet() { this.invocationProperties = new HashMap(); } /** * Used by {@link #createResponse(Message)} and {@link #copy(boolean)}. */ private Packet(Packet that) { relatePackets(that, true); this.invocationProperties = that.invocationProperties; } /** * Creates a copy of this {@link Packet}. * * @param copyMessage determines whether the {@link Message} from the original {@link Packet} should be copied as * well, or not. If the value is {@code false}, the {@link Message} in the copy of the {@link Packet} is {@code null}. * @return copy of the original packet */ public Packet copy(boolean copyMessage) { // the copy constructor is originally designed for creating a response packet, // but so far the implementation is usable for this purpose as well, so calling the copy constructor // to avoid code dupliation. Packet copy = new Packet(this); if (copyMessage && this.message != null) { copy.message = this.message.copy(); } if (copy.message != null) copy.message.setMessageMedadata(copy); return copy; } private Message message; /** * Gets the last {@link Message} set through {@link #setMessage(Message)}. * * @return may null. See the class javadoc for when it's null. */ public Message getMessage() { if (message != null && !(message instanceof MessageWrapper)) { message = new MessageWrapper(this, message); } return message; } public Message getInternalMessage() { return (message instanceof MessageWrapper)? ((MessageWrapper)message).delegate : message; } public WSBinding getBinding() { if (endpoint != null) { return endpoint.getBinding(); } if (proxy != null) { return (WSBinding) proxy.getBinding(); } return null; } /** * Sets a {@link Message} to this packet. * * @param message Can be null. */ public void setMessage(Message message) { this.message = message; if (message != null) this.message.setMessageMedadata(this); } private WSDLOperationMapping wsdlOperationMapping = null; private QName wsdlOperation; /** * Returns the QName of the wsdl operation associated with this packet. *

* Information such as Payload QName, wsa:Action header, SOAPAction HTTP header are used depending on the features * enabled on the particular port. * * @return null if there is no WSDL model or * runtime cannot uniquely identify the wsdl operation from the information in the packet. */ @Property(MessageContext.WSDL_OPERATION) public final @Nullable QName getWSDLOperation() { if (wsdlOperation != null) return wsdlOperation; if ( wsdlOperationMapping == null) wsdlOperationMapping = getWSDLOperationMapping(); if ( wsdlOperationMapping != null ) wsdlOperation = wsdlOperationMapping.getOperationName(); return wsdlOperation; } public WSDLOperationMapping getWSDLOperationMapping() { if (wsdlOperationMapping != null) return wsdlOperationMapping; OperationDispatcher opDispatcher = null; if (endpoint != null) { opDispatcher = endpoint.getOperationDispatcher(); } else if (proxy != null) { opDispatcher = ((Stub) proxy).getOperationDispatcher(); } //OpDispatcher is null when there is no WSDLModel if (opDispatcher != null) { try { wsdlOperationMapping = opDispatcher.getWSDLOperationMapping(this); } catch (DispatchException e) { //Ignore, this might be a protocol message which may not have a wsdl operation //LOGGER.info("Cannot resolve wsdl operation that this Packet is targeted for."); } } return wsdlOperationMapping; } /** * Set the wsdl operation to avoid lookup from other data. * This is useful in SEI based clients, where the WSDL operation can be known * from the associated {@link JavaMethod} * * @param wsdlOp QName */ public void setWSDLOperation(QName wsdlOp) { this.wsdlOperation = wsdlOp; } /** * True if this message came from a transport (IOW inbound), * and in paricular from a "secure" transport. A transport * needs to set this flag appropriately. * *

* This is a requirement from the security team. */ // TODO: expose this as a property public boolean wasTransportSecure; /** * Inbound transport headers are captured in a transport neutral way. * Transports are expected to fill this data after creating a Packet. *

* {@link SOAPMessage#getMimeHeaders()} would return these headers. */ public static final String INBOUND_TRANSPORT_HEADERS = "com.sun.xml.internal.ws.api.message.packet.inbound.transport.headers"; /** * Outbound transport headers are captured in a transport neutral way. * *

* Transports may choose to ignore certain headers that interfere with * its correct operation, such as * Content-Type and Content-Length. */ public static final String OUTBOUND_TRANSPORT_HEADERS = "com.sun.xml.internal.ws.api.message.packet.outbound.transport.headers"; /** * */ public static final String HA_INFO = "com.sun.xml.internal.ws.api.message.packet.hainfo"; /** * This property holds the snapshot of HandlerConfiguration * at the time of invocation. * This property is used by MUPipe and HandlerPipe implementations. */ @Property(BindingProviderProperties.JAXWS_HANDLER_CONFIG) public HandlerConfiguration handlerConfig; /** * If a message originates from a proxy stub that implements * a port interface, this field is set to point to that object. * * TODO: who's using this property? */ @Property(BindingProviderProperties.JAXWS_CLIENT_HANDLE_PROPERTY) public BindingProvider proxy; /** * Determines if the governing {@link Adapter} or {@link com.sun.xml.internal.ws.api.pipe.Fiber.CompletionCallback} * will handle delivering response messages targeted at non-anonymous endpoint * addresses. Prior to the introduction of this flag * the {@link WsaServerTube} would deliver non-anonymous responses. */ public boolean isAdapterDeliversNonAnonymousResponse; /** * During invocation of a client Stub or Dispatch a Packet is * created then the Stub's RequestContext is copied into the * Packet. On certain internal cases the Packet is created * *before* the invocation. In those cases we want the contents * of the Packet to take precedence when ever any key/value pairs * collide : if the Packet contains a value for a key use it, * otherwise copy as usual from Stub. */ public boolean packetTakesPriorityOverRequestContext = false; /** * The endpoint address to which this message is sent to. * *

* The JAX-WS spec allows this to be changed for each message, * so it's designed to be a property. * *

* Must not be null for a request message on the client. Otherwise * it's null. */ public EndpointAddress endpointAddress; /** * @deprecated * The programatic acccess should be done via * {@link #endpointAddress}. This is for JAX-WS client applications * that access this property via {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}. */ @Property(BindingProvider.ENDPOINT_ADDRESS_PROPERTY) public String getEndPointAddressString() { if (endpointAddress == null) { return null; } else { return endpointAddress.toString(); } } public void setEndPointAddressString(String s) { if (s == null) { this.endpointAddress = null; } else { this.endpointAddress = EndpointAddress.create(s); } } /** * The value of {@link ContentNegotiation#PROPERTY} * property. *

* This property is used only on the client side. */ public ContentNegotiation contentNegotiation; @Property(ContentNegotiation.PROPERTY) public String getContentNegotiationString() { return (contentNegotiation != null) ? contentNegotiation.toString() : null; } public void setContentNegotiationString(String s) { if (s == null) { contentNegotiation = null; } else { try { contentNegotiation = ContentNegotiation.valueOf(s); } catch (IllegalArgumentException e) { // If the value is not recognized default to none contentNegotiation = ContentNegotiation.none; } } } /** * Gives a list of Reference Parameters in the Message *

* Headers which have attribute wsa:IsReferenceParameter="true" * This is not cached as one may reset the Message. *

*/ @Property(MessageContext.REFERENCE_PARAMETERS) public @NotNull List getReferenceParameters() { Message msg = getMessage(); List refParams = new ArrayList(); if (msg == null) { return refParams; } MessageHeaders hl = msg.getHeaders(); for (Header h : hl.asList()) { String attr = h.getAttribute(AddressingVersion.W3C.nsUri, "IsReferenceParameter"); if (attr != null && (attr.equals("true") || attr.equals("1"))) { Document d = DOMUtil.createDom(); SAX2DOMEx s2d = new SAX2DOMEx(d); try { h.writeTo(s2d, XmlUtil.DRACONIAN_ERROR_HANDLER); refParams.add((Element) d.getLastChild()); } catch (SAXException e) { throw new WebServiceException(e); } /* DOMResult result = new DOMResult(d); XMLDOMWriterImpl domwriter = new XMLDOMWriterImpl(result); try { h.writeTo(domwriter); refParams.add((Element) result.getNode().getLastChild()); } catch (XMLStreamException e) { throw new WebServiceException(e); } */ } } return refParams; } /** * This method is for exposing header list through {@link PropertySet#get(Object)}, * for user applications, and should never be invoked directly from within the JAX-WS RI. */ @Property(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY) /*package*/ MessageHeaders getHeaderList() { Message msg = getMessage(); if (msg == null) { return null; } return msg.getHeaders(); } /** * The list of MIME types that are acceptable to a receiver * of an outbound message. * * This property is used only on the server side. * *

The representation shall be that specified by the HTTP Accept * request-header field. * *

The list of content types will be obtained from the transport * meta-data of a inbound message in a request/response message exchange. * Hence this property will be set by the service-side transport pipe. */ public String acceptableMimeTypes; /** * When non-null, this object is consulted to * implement {@link WebServiceContext} methods * exposed to the user application. * * Used only on the server side. * *

* This property is set from the parameter * of {@link WSEndpoint.PipeHead#process}. */ public WebServiceContextDelegate webServiceContextDelegate; /** * Used only on the server side so that the transport * can close the connection early. * *

* This field can be null. While a message is being processed, * this field can be set explicitly to null, to prevent * future pipes from closing a transport (see {@link #keepTransportBackChannelOpen()}) * *

* This property is set from the parameter * of {@link WSEndpoint.PipeHead#process}. */ public @Nullable TransportBackChannel transportBackChannel; /** * Keeps the transport back channel open (by seeting {@link #transportBackChannel} to null.) * * @return * The previous value of {@link #transportBackChannel}. */ public TransportBackChannel keepTransportBackChannelOpen() { TransportBackChannel r = transportBackChannel; transportBackChannel = null; return r; } /** * The governing owner of this packet. On the service-side this is the {@link Adapter} and on the client it is the {@link Stub}. * */ public Component component; /** * The governing {@link WSEndpoint} in which this message is floating. * *

* This property is set if and only if this is on the server side. */ @Property(JAXWSProperties.WSENDPOINT) public WSEndpoint endpoint; /** * The value of the SOAPAction header associated with the message. * *

* For outgoing messages, the transport may sends out this value. * If this field is null, the transport may choose to send "" * (quoted empty string.) * * For incoming messages, the transport will set this field. * If the incoming message did not contain the SOAPAction header, * the transport sets this field to null. * *

* If the value is non-null, it must be always in the quoted form. * The value can be null. * *

* Note that the way the transport sends this value out depends on * transport and SOAP version. *

* For HTTP transport and SOAP 1.1, BP requires that SOAPAction * header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2, * this is moved to the parameter of the "application/soap+xml". */ @Property(BindingProvider.SOAPACTION_URI_PROPERTY) public String soapAction; /** * A hint indicating that whether a transport should expect * a reply back from the server. * *

* This property is used on the client-side for * outbound messages, so that a pipeline * can communicate to the terminal (or intermediate) {@link Tube}s * about this knowledge. * *

* This property MUST NOT be used by 2-way transports * that have the transport back channel. Those transports * must always check a reply coming through the transport back * channel regardless of this value, and act accordingly. * (This is because the expectation of the client and * that of the server can be different, for example because * of a bug in user's configuration.) * *

* This property is for one-way transports, and more * specifically for the coordinator that correlates sent requests * and incoming replies, to decide whether to block * until a response is received. * *

* Also note that this property is related to * {@link WSDLOperation#isOneWay()} but not the same thing. * In fact in general, they are completely orthogonal. * * For example, the calling application can choose to invoke * {@link Dispatch#invoke(Object)} or {@link Dispatch#invokeOneWay(Object)} * with an operation (which determines the value of this property), * regardless of whether WSDL actually says it's one way or not. * So these two booleans can take any combinations. * * *

* When this property is {@link Boolean#FALSE}, it means that * the pipeline does not expect a reply from a server (and therefore * the correlator should not block for a reply message * -- if such a reply does arrive, it can be just ignored.) * *

* When this property is {@link Boolean#TRUE}, it means that * the pipeline expects a reply from a server (and therefore * the correlator should block to see if a reply message is received, * *

* This property is always set to {@link Boolean#TRUE} or * {@link Boolean#FALSE} when used on the request message * on the client side. * No other {@link Boolean} instances are allowed. *

* * In all other situations, this property is null. * */ @Property(BindingProviderProperties.ONE_WAY_OPERATION) public Boolean expectReply; /** * This property will be removed in a near future. * *

* A part of what this flag represented moved to * {@link #expectReply} and the other part was moved * to {@link Message#isOneWay(WSDLPort)}. Please update * your code soon, or risk breaking your build!! */ @Deprecated public Boolean isOneWay; /** * Indicates whether is invoking a synchronous pattern. If true, no * async client programming model (e.g. AsyncResponse or AsyncHandler) * were used to make the request that created this packet. */ public Boolean isSynchronousMEP; /** * Indicates whether a non-null AsyncHandler was given at the point of * making the request that created this packet. This flag can be used * by Tube implementations to decide how to react when isSynchronousMEP * is false. If true, the client gave a non-null AsyncHandler instance * at the point of request, and will be expecting a response on that * handler when this request has been processed. */ public Boolean nonNullAsyncHandlerGiven; /** * USE-CASE: * WS-AT is enabled, but there is no WSDL available. * If Packet.isRequestReplyMEP() is Boolean.TRUE then WS-AT should * add the TX context. * * This value is exposed to users via facades at higher abstraction layers. * The user should NEVER use Packet directly. * This value should ONLY be set by users. */ private Boolean isRequestReplyMEP; public Boolean isRequestReplyMEP() { return isRequestReplyMEP; } public void setRequestReplyMEP(final Boolean x) { isRequestReplyMEP = x; } /** * Lazily created set of handler-scope property names. * *

* We expect that this is only used when handlers are present * and they explicitly set some handler-scope values. * * @see #getHandlerScopePropertyNames(boolean) */ private Set handlerScopePropertyNames; /** * Bag to capture properties that are available for the whole * message invocation (namely on both requests and responses.) * *

* These properties are copied from a request to a response. * This is where we keep properties that are set by handlers. * *

* See class javadoc for more discussion. * * @see #getHandlerScopePropertyNames(boolean) */ public final Map invocationProperties; /** * Gets a {@link Set} that stores handler-scope properties. * *

* These properties will not be exposed to the response context. * Consequently, if a {@link Tube} wishes to hide a property * to {@link ResponseContext}, it needs to add the property name * to this set. * * @param readOnly * Return true if the caller only intends to read the value of this set. * Internally, the {@link Set} is allocated lazily, and this flag helps * optimizing the strategy. * * @return * always non-null, possibly empty set that stores property names. */ public final Set getHandlerScopePropertyNames(boolean readOnly) { Set o = this.handlerScopePropertyNames; if (o == null) { if (readOnly) { return Collections.emptySet(); } o = new HashSet(); this.handlerScopePropertyNames = o; } return o; } /** * This method no longer works. * * @deprecated * Use {@link #getHandlerScopePropertyNames(boolean)}. * To be removed once Tango components are updated. */ public final Set getApplicationScopePropertyNames(boolean readOnly) { assert false; return new HashSet(); } /** * Creates a response {@link Packet} from a request packet ({@code this}). * *

* When a {@link Packet} for a reply is created, some properties need to be * copied over from a request to a response, and this method handles it correctly. * * @deprecated * Use createClientResponse(Message) for client side and * createServerResponse(Message, String) for server side response * creation. * * @param msg * The {@link Message} that represents a reply. Can be null. */ @Deprecated public Packet createResponse(Message msg) { Packet response = new Packet(this); response.setMessage(msg); return response; } /** * Creates a response {@link Packet} from a request packet ({@code this}). * *

* When a {@link Packet} for a reply is created, some properties need to be * copied over from a request to a response, and this method handles it correctly. * * @param msg * The {@link Message} that represents a reply. Can be null. */ public Packet createClientResponse(Message msg) { Packet response = new Packet(this); response.setMessage(msg); finishCreateRelateClientResponse(response); return response; } /** * For use cases that start with an existing Packet. */ public Packet relateClientResponse(final Packet response) { response.relatePackets(this, true); finishCreateRelateClientResponse(response); return response; } private void finishCreateRelateClientResponse(final Packet response) { response.soapAction = null; // de-initializing response.setState(State.ClientResponse); } /** * Creates a server-side response {@link Packet} from a request * packet ({@code this}). If WS-Addressing is enabled, a default Action * Message Addressing Property is obtained using wsdlPort {@link WSDLPort} * and binding {@link WSBinding}. *

* This method should be called to create application response messages * since they are associated with a {@link WSBinding} and {@link WSDLPort}. * For creating protocol messages that require a non-default Action, use * {@link #createServerResponse(Message, com.sun.xml.internal.ws.api.addressing.AddressingVersion, com.sun.xml.internal.ws.api.SOAPVersion, String)}. * * @param responseMessage The {@link Message} that represents a reply. Can be null. * @param wsdlPort The response WSDL port. * @param binding The response Binding. Cannot be null. * @return response packet */ public Packet createServerResponse(@Nullable Message responseMessage, @Nullable WSDLPort wsdlPort, @Nullable SEIModel seiModel, @NotNull WSBinding binding) { Packet r = createClientResponse(responseMessage); return relateServerResponse(r, wsdlPort, seiModel, binding); } /** * Copy all properties from ({@code this}) packet into a input {@link Packet} * @param response packet */ public void copyPropertiesTo(@Nullable Packet response){ relatePackets(response, false); } /** * A common method to make members related between input packet and this packet * * @param packet * @param isCopy 'true' means copying all properties from input packet; * 'false' means copying all properties from this packet to input packet. */ private void relatePackets(@Nullable Packet packet, boolean isCopy) { Packet request; Packet response; if (!isCopy) { //is relate request = this; response = packet; // processing specific properties response.soapAction = null; response.invocationProperties.putAll(request.invocationProperties); if (this.getState().equals(State.ServerRequest)) { response.setState(State.ServerResponse); } } else { //is copy constructor request = packet; response = this; // processing specific properties response.soapAction = request.soapAction; response.setState(request.getState()); } request.copySatelliteInto(response); response.isAdapterDeliversNonAnonymousResponse = request.isAdapterDeliversNonAnonymousResponse; response.handlerConfig = request.handlerConfig; response.handlerScopePropertyNames = request.handlerScopePropertyNames; response.contentNegotiation = request.contentNegotiation; response.wasTransportSecure = request.wasTransportSecure; response.transportBackChannel = request.transportBackChannel; response.endpointAddress = request.endpointAddress; response.wsdlOperation = request.wsdlOperation; response.wsdlOperationMapping = request.wsdlOperationMapping; response.acceptableMimeTypes = request.acceptableMimeTypes; response.endpoint = request.endpoint; response.proxy = request.proxy; response.webServiceContextDelegate = request.webServiceContextDelegate; response.expectReply = request.expectReply; response.component = request.component; response.mtomAcceptable = request.mtomAcceptable; response.mtomRequest = request.mtomRequest; // copy other properties that need to be copied. is there any? } public Packet relateServerResponse(@Nullable Packet r, @Nullable WSDLPort wsdlPort, @Nullable SEIModel seiModel, @NotNull WSBinding binding) { relatePackets(r, false); r.setState(State.ServerResponse); AddressingVersion av = binding.getAddressingVersion(); // populate WS-A headers only if WS-A is enabled if (av == null) { return r; } if (getMessage() == null) { return r; } //populate WS-A headers only if the request has addressing headers String inputAction = AddressingUtils.getAction(getMessage().getHeaders(), av, binding.getSOAPVersion()); if (inputAction == null) { return r; } // if one-way, then dont populate any WS-A headers if (r.getMessage() == null || (wsdlPort != null && getMessage().isOneWay(wsdlPort))) { return r; } // otherwise populate WS-Addressing headers populateAddressingHeaders(binding, r, wsdlPort, seiModel); return r; } /** * Creates a server-side response {@link Packet} from a request * packet ({@code this}). If WS-Addressing is enabled, action * is used as Action Message Addressing Property. *

* This method should be called only for creating protocol response messages * that require a particular value of Action since they are not associated * with a {@link WSBinding} and {@link WSDLPort} but do know the {@link AddressingVersion} * and {@link SOAPVersion}. * * @param responseMessage The {@link Message} that represents a reply. Can be null. * @param addressingVersion The WS-Addressing version of the response message. * @param soapVersion The SOAP version of the response message. * @param action The response Action Message Addressing Property value. * @return response packet */ public Packet createServerResponse(@Nullable Message responseMessage, @NotNull AddressingVersion addressingVersion, @NotNull SOAPVersion soapVersion, @NotNull String action) { Packet responsePacket = createClientResponse(responseMessage); responsePacket.setState(State.ServerResponse); // populate WS-A headers only if WS-A is enabled if (addressingVersion == null) { return responsePacket; } //populate WS-A headers only if the request has addressing headers String inputAction = AddressingUtils.getAction(this.getMessage().getHeaders(), addressingVersion, soapVersion); if (inputAction == null) { return responsePacket; } populateAddressingHeaders(responsePacket, addressingVersion, soapVersion, action, false); return responsePacket; } /** * Overwrites the {@link Message} of the response packet ({@code this}) by the given {@link Message}. * Unlike {@link #setMessage(Message)}, fill in the addressing headers correctly, and this process * requires the access to the request packet. * *

* This method is useful when the caller needs to swap a response message completely to a new one. * * @see #createServerResponse(Message, AddressingVersion, SOAPVersion, String) */ public void setResponseMessage(@NotNull Packet request, @Nullable Message responseMessage, @NotNull AddressingVersion addressingVersion, @NotNull SOAPVersion soapVersion, @NotNull String action) { Packet temp = request.createServerResponse(responseMessage, addressingVersion, soapVersion, action); setMessage(temp.getMessage()); } private void populateAddressingHeaders(Packet responsePacket, AddressingVersion av, SOAPVersion sv, String action, boolean mustUnderstand) { // populate WS-A headers only if WS-A is enabled if (av == null) return; // if one-way, then dont populate any WS-A headers if (responsePacket.getMessage() == null) return; MessageHeaders hl = responsePacket.getMessage().getHeaders(); WsaPropertyBag wpb = getSatellite(WsaPropertyBag.class); Message msg = getMessage(); // wsa:To WSEndpointReference replyTo = null; Header replyToFromRequestMsg = AddressingUtils.getFirstHeader(msg.getHeaders(), av.replyToTag, true, sv); Header replyToFromResponseMsg = hl.get(av.toTag, false); boolean replaceToTag = true; try{ if (replyToFromRequestMsg != null){ replyTo = replyToFromRequestMsg.readAsEPR(av); } if (replyToFromResponseMsg != null && replyTo == null) { replaceToTag = false; } } catch (XMLStreamException e) { throw new WebServiceException(AddressingMessages.REPLY_TO_CANNOT_PARSE(), e); } if (replyTo == null) { replyTo = AddressingUtils.getReplyTo(msg.getHeaders(), av, sv); } // wsa:Action, add if the message doesn't already contain it, // generally true for SEI case where there is SEIModel or WSDLModel // false for Provider with no wsdl, Expects User to set the coresponding header on the Message. if (AddressingUtils.getAction(responsePacket.getMessage().getHeaders(), av, sv) == null) { //wsa:Action header is not set in the message, so use the wsa:Action passed as the parameter. hl.add(new StringHeader(av.actionTag, action, sv, mustUnderstand)); } // wsa:MessageID if (responsePacket.getMessage().getHeaders().get(av.messageIDTag, false) == null) { // if header doesn't exist, method getID creates a new random id String newID = Message.generateMessageID(); hl.add(new StringHeader(av.messageIDTag, newID)); } // wsa:RelatesTo String mid = null; if (wpb != null) { mid = wpb.getMessageID(); } if (mid == null) { mid = AddressingUtils.getMessageID(msg.getHeaders(), av, sv); } if (mid != null) { hl.addOrReplace(new RelatesToHeader(av.relatesToTag, mid)); } // populate reference parameters WSEndpointReference refpEPR = null; if (responsePacket.getMessage().isFault()) { // choose FaultTo if (wpb != null) { refpEPR = wpb.getFaultToFromRequest(); } if (refpEPR == null) { refpEPR = AddressingUtils.getFaultTo(msg.getHeaders(), av, sv); } // if FaultTo is null, then use ReplyTo if (refpEPR == null) { refpEPR = replyTo; } } else { // choose ReplyTo refpEPR = replyTo; } if (replaceToTag && refpEPR != null) { hl.addOrReplace(new StringHeader(av.toTag, refpEPR.getAddress())); refpEPR.addReferenceParametersToList(hl); } } private void populateAddressingHeaders(WSBinding binding, Packet responsePacket, WSDLPort wsdlPort, SEIModel seiModel) { AddressingVersion addressingVersion = binding.getAddressingVersion(); if (addressingVersion == null) { return; } WsaTubeHelper wsaHelper = addressingVersion.getWsaHelper(wsdlPort, seiModel, binding); String action = responsePacket.getMessage().isFault() ? wsaHelper.getFaultAction(this, responsePacket) : wsaHelper.getOutputAction(this); if (action == null) { LOGGER.info("WSA headers are not added as value for wsa:Action cannot be resolved for this message"); return; } populateAddressingHeaders(responsePacket, addressingVersion, binding.getSOAPVersion(), action, AddressingVersion.isRequired(binding)); } public String toShortString() { return super.toString(); } // For use only in a debugger @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append(super.toString()); String content; try { Message msg = getMessage(); if (msg != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); XMLStreamWriter xmlWriter = XMLStreamWriterFactory.create(baos, "UTF-8"); msg.copy().writeTo(xmlWriter); xmlWriter.flush(); xmlWriter.close(); baos.flush(); XMLStreamWriterFactory.recycle(xmlWriter); byte[] bytes = baos.toByteArray(); //message = Messages.create(XMLStreamReaderFactory.create(null, new ByteArrayInputStream(bytes), "UTF-8", true)); content = new String(bytes, "UTF-8"); } else { content = ""; } } catch (Throwable t) { throw new WebServiceException(t); } buf.append(" Content: ").append(content); return buf.toString(); } // completes TypedMap private static final PropertyMap model; static { model = parse(Packet.class); } @Override protected PropertyMap getPropertyMap() { return model; } public Map asMapIncludingInvocationProperties() { final Map asMap = asMap(); return new AbstractMap() { @Override public Object get(Object key) { Object o = asMap.get(key); if (o != null) return o; return invocationProperties.get(key); } @Override public int size() { return asMap.size() + invocationProperties.size(); } @Override public boolean containsKey(Object key) { if (asMap.containsKey(key)) return true; return invocationProperties.containsKey(key); } @Override public Set> entrySet() { final Set> asMapEntries = asMap.entrySet(); final Set> ipEntries = invocationProperties.entrySet(); return new AbstractSet>() { @Override public Iterator> iterator() { final Iterator> asMapIt = asMapEntries.iterator(); final Iterator> ipIt = ipEntries.iterator(); return new Iterator>() { @Override public boolean hasNext() { return asMapIt.hasNext() || ipIt.hasNext(); } @Override public java.util.Map.Entry next() { if (asMapIt.hasNext()) return asMapIt.next(); return ipIt.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public int size() { return asMap.size() + invocationProperties.size(); } }; } @Override public Object put(String key, Object value) { if (supports(key)) return asMap.put(key, value); return invocationProperties.put(key, value); } @Override public void clear() { asMap.clear(); invocationProperties.clear(); } @Override public Object remove(Object key) { if (supports(key)) return asMap.remove(key); return invocationProperties.remove(key); } }; } private static final Logger LOGGER = Logger.getLogger(Packet.class.getName()); @Override public SOAPMessage getSOAPMessage() throws SOAPException { return getAsSOAPMessage(); } //TODO replace the message to a SAAJMEssage issue - JRFSAAJMessage or SAAJMessage? @Override public SOAPMessage getAsSOAPMessage() throws SOAPException { Message msg = this.getMessage(); if (msg == null) return null; if (msg instanceof MessageWritable) ((MessageWritable) msg).setMTOMConfiguration(mtomFeature); return msg.readAsSOAPMessage(this, this.getState().isInbound()); } public Codec codec = null; public Codec getCodec() { if (codec != null) { return codec; } if (endpoint != null) { codec = endpoint.createCodec(); } WSBinding wsb = getBinding(); if (wsb != null) { codec = wsb.getBindingId().createEncoder(wsb); } return codec; } @Override public com.oracle.webservices.internal.api.message.ContentType writeTo( OutputStream out ) throws IOException { Message msg = getInternalMessage(); if (msg instanceof MessageWritable) { ((MessageWritable) msg).setMTOMConfiguration(mtomFeature); return ((MessageWritable)msg).writeTo(out); } return getCodec().encode(this, out); } public com.oracle.webservices.internal.api.message.ContentType writeTo( WritableByteChannel buffer ) { return getCodec().encode(this, buffer); } private ContentType contentType; /** * If the request's Content-Type is multipart/related; type=application/xop+xml, then this set to to true * * Used on server-side, for encoding the repsonse. */ private Boolean mtomRequest; /** * Based on request's Accept header this is set. * Currently only set if MTOMFeature is enabled. * * Should be used on server-side, for encoding the response. */ private Boolean mtomAcceptable; private MTOMFeature mtomFeature; public Boolean getMtomRequest() { return mtomRequest; } public void setMtomRequest(Boolean mtomRequest) { this.mtomRequest = mtomRequest; } public Boolean getMtomAcceptable() { return mtomAcceptable; } Boolean checkMtomAcceptable; public void checkMtomAcceptable() { if (checkMtomAcceptable == null) { if (acceptableMimeTypes == null || isFastInfosetDisabled) { checkMtomAcceptable = false; } else { checkMtomAcceptable = (acceptableMimeTypes.indexOf(MtomCodec.XOP_XML_MIME_TYPE) != -1); // StringTokenizer st = new StringTokenizer(acceptableMimeTypes, ","); // while (st.hasMoreTokens()) { // final String token = st.nextToken().trim(); // if (token.toLowerCase().contains(MtomCodec.XOP_XML_MIME_TYPE)) { // mtomAcceptable = true; // } // } // if (mtomAcceptable == null) mtomAcceptable = false; } } mtomAcceptable = checkMtomAcceptable; } private Boolean fastInfosetAcceptable; public Boolean getFastInfosetAcceptable(String fiMimeType) { if (fastInfosetAcceptable == null) { if (acceptableMimeTypes == null || isFastInfosetDisabled) { fastInfosetAcceptable = false; } else { fastInfosetAcceptable = (acceptableMimeTypes.indexOf(fiMimeType) != -1); } // if (accept == null || isFastInfosetDisabled) return false; // // StringTokenizer st = new StringTokenizer(accept, ","); // while (st.hasMoreTokens()) { // final String token = st.nextToken().trim(); // if (token.equalsIgnoreCase(fiMimeType)) { // return true; // } // } // return false; } return fastInfosetAcceptable; } public void setMtomFeature(MTOMFeature mtomFeature) { this.mtomFeature = mtomFeature; } public MTOMFeature getMtomFeature() { //If we have a binding, use that in preference to an explicitly //set MTOMFeature WSBinding binding = getBinding(); if (binding != null) { return binding.getFeature(MTOMFeature.class); } return mtomFeature; } @Override public com.oracle.webservices.internal.api.message.ContentType getContentType() { if (contentType == null) { contentType = getInternalContentType(); } if (contentType == null) { contentType = getCodec().getStaticContentType(this); } if (contentType == null) { //TODO write to buffer } return contentType; } public ContentType getInternalContentType() { Message msg = getInternalMessage(); if (msg instanceof MessageWritable) { return ((MessageWritable)msg).getContentType(); } return contentType; } public void setContentType(ContentType contentType) { this.contentType = contentType; } public enum Status { Request, Response, Unknown; public boolean isRequest() { return Request.equals(this); } public boolean isResponse() { return Response.equals(this); } } public enum State { ServerRequest(true), ClientRequest(false), ServerResponse(false), ClientResponse(true); private boolean inbound; State(boolean inbound) { this.inbound = inbound; } public boolean isInbound() { return inbound; } } // private Status status = Status.Unknown; //Default state is ServerRequest - some custom adapters may not set the value of state //upon server request - all other code paths should set it private State state = State.ServerRequest; // public Status getStatus() { return status; } public State getState() { return state; } public void setState(State state) { this.state = state; } public boolean shouldUseMtom() { if (getState().isInbound()) { return isMtomContentType(); } else { return shouldUseMtomOutbound(); } } private boolean shouldUseMtomOutbound() { //Use the getter to make sure all the logic is executed correctly MTOMFeature myMtomFeature = getMtomFeature(); if(myMtomFeature != null && myMtomFeature.isEnabled()) { //On client, always use XOP encoding if MTOM is enabled //On Server, mtomAcceptable and mtomRequest will be set - use XOP encoding //if either request is XOP encoded (mtomRequest) or //client accepts XOP encoding (mtomAcceptable) if (getMtomAcceptable() == null && getMtomRequest() == null) { return true; } else { if (getMtomAcceptable() != null && getMtomAcceptable() && getState().equals(State.ServerResponse)) { return true; } if (getMtomRequest() != null && getMtomRequest() && getState().equals(State.ServerResponse)) { return true; } } } return false; } private boolean isMtomContentType() { return (getInternalContentType() != null) && (getInternalContentType().getContentType().contains("application/xop+xml")); } /** * @deprecated */ public void addSatellite(@NotNull com.sun.xml.internal.ws.api.PropertySet satellite) { super.addSatellite(satellite); } /** * @deprecated */ public void addSatellite(@NotNull Class keyClass, @NotNull com.sun.xml.internal.ws.api.PropertySet satellite) { super.addSatellite(keyClass, satellite); } /** * @deprecated */ public void copySatelliteInto(@NotNull com.sun.xml.internal.ws.api.DistributedPropertySet r) { super.copySatelliteInto(r); } /** * @deprecated */ public void removeSatellite(com.sun.xml.internal.ws.api.PropertySet satellite) { super.removeSatellite(satellite); } /** * This is propogated from SOAPBindingCodec and will affect isMtomAcceptable and isFastInfosetAcceptable */ private boolean isFastInfosetDisabled; public void setFastInfosetDisabled(boolean b) { isFastInfosetDisabled = b; } }