aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.ws.api.message; aoqi@0: aoqi@0: import com.oracle.webservices.internal.api.message.ContentType; aoqi@0: import com.oracle.webservices.internal.api.message.PropertySet; aoqi@0: import com.sun.istack.internal.NotNull; aoqi@0: import com.sun.istack.internal.Nullable; aoqi@0: import com.sun.xml.internal.bind.marshaller.SAX2DOMEx; aoqi@0: import com.sun.xml.internal.ws.addressing.WsaPropertyBag; aoqi@0: import com.sun.xml.internal.ws.addressing.WsaServerTube; aoqi@0: import com.sun.xml.internal.ws.addressing.WsaTubeHelper; aoqi@0: import com.sun.xml.internal.ws.api.Component; aoqi@0: import com.sun.xml.internal.ws.api.EndpointAddress; aoqi@0: import com.sun.xml.internal.ws.api.SOAPVersion; aoqi@0: import com.sun.xml.internal.ws.api.WSBinding; aoqi@0: import com.sun.xml.internal.ws.api.addressing.AddressingVersion; aoqi@0: import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; aoqi@0: import com.sun.xml.internal.ws.api.model.JavaMethod; aoqi@0: import com.sun.xml.internal.ws.api.model.SEIModel; aoqi@0: import com.sun.xml.internal.ws.api.model.WSDLOperationMapping; aoqi@0: import com.sun.xml.internal.ws.api.model.wsdl.WSDLOperation; aoqi@0: import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; aoqi@0: import com.sun.xml.internal.ws.api.pipe.Codec; aoqi@0: import com.sun.xml.internal.ws.api.pipe.Tube; aoqi@0: import com.sun.xml.internal.ws.api.server.Adapter; aoqi@0: import com.sun.xml.internal.ws.api.server.TransportBackChannel; aoqi@0: import com.sun.xml.internal.ws.api.server.WSEndpoint; aoqi@0: import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate; aoqi@0: import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory; aoqi@0: import com.sun.xml.internal.ws.client.*; aoqi@0: import com.sun.xml.internal.ws.developer.JAXWSProperties; aoqi@0: import com.sun.xml.internal.ws.encoding.MtomCodec; aoqi@0: import com.sun.xml.internal.ws.message.RelatesToHeader; aoqi@0: import com.sun.xml.internal.ws.message.StringHeader; aoqi@0: import com.sun.xml.internal.ws.util.DOMUtil; aoqi@0: import com.sun.xml.internal.ws.util.xml.XmlUtil; aoqi@0: import com.sun.xml.internal.ws.wsdl.DispatchException; aoqi@0: import com.sun.xml.internal.ws.wsdl.OperationDispatcher; aoqi@0: import com.sun.xml.internal.ws.resources.AddressingMessages; aoqi@0: aoqi@0: aoqi@0: import org.w3c.dom.Document; aoqi@0: import org.w3c.dom.Element; aoqi@0: import org.xml.sax.SAXException; aoqi@0: aoqi@0: import javax.xml.namespace.QName; aoqi@0: import javax.xml.soap.SOAPException; aoqi@0: import javax.xml.soap.SOAPMessage; aoqi@0: import javax.xml.stream.XMLStreamWriter; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: import javax.xml.ws.BindingProvider; aoqi@0: import javax.xml.ws.Dispatch; aoqi@0: import javax.xml.ws.WebServiceContext; aoqi@0: import javax.xml.ws.WebServiceException; aoqi@0: import javax.xml.ws.handler.LogicalMessageContext; aoqi@0: import javax.xml.ws.handler.MessageContext; aoqi@0: import javax.xml.ws.handler.soap.SOAPMessageContext; aoqi@0: import javax.xml.ws.soap.MTOMFeature; aoqi@0: aoqi@0: import java.util.*; aoqi@0: import java.util.logging.Logger; aoqi@0: import java.io.ByteArrayOutputStream; aoqi@0: import java.io.IOException; aoqi@0: import java.io.OutputStream; aoqi@0: import java.nio.channels.WritableByteChannel; aoqi@0: aoqi@0: /** aoqi@0: * Represents a container of a {@link Message}. aoqi@0: * aoqi@0: *
aoqi@0: * A packet can be thought of as a frame/envelope/package that wraps aoqi@0: * a {@link Message}. A packet keeps track of optional metadata (properties) aoqi@0: * about a {@link Message} that doesn't go across the wire. aoqi@0: * This roughly corresponds to {@link MessageContext} in the JAX-WS API. aoqi@0: * aoqi@0: *
aoqi@0: * Usually a packet contains a {@link Message} in it, but sometimes aoqi@0: * (such as for a reply of an one-way operation), a packet may aoqi@0: * float around without a {@link Message} in it. aoqi@0: * aoqi@0: * aoqi@0: * aoqi@0: *
aoqi@0: * Information frequently used inside the JAX-WS RI aoqi@0: * is stored in the strongly-typed fields. Other information is stored aoqi@0: * in terms of a generic {@link Map} (see aoqi@0: * {@link #invocationProperties}.) aoqi@0: * aoqi@0: *
aoqi@0: * Some properties need to be retained between request and response, aoqi@0: * some don't. For strongly typed fields, this characteristic is aoqi@0: * statically known for each of them, and propagation happens accordingly. aoqi@0: * For generic information stored in {@link Map}, {@link #invocationProperties} aoqi@0: * stores per-invocation scope information (which carries over to aoqi@0: * the response.) aoqi@0: * aoqi@0: *
aoqi@0: * This object is used as the backing store of {@link MessageContext}, and aoqi@0: * {@link LogicalMessageContext} and {@link SOAPMessageContext} will aoqi@0: * be delegating to this object for storing/retrieving values. aoqi@0: * aoqi@0: * aoqi@0: *
aoqi@0: * {@link BindingProvider#getRequestContext() Request context} is used to aoqi@0: * seed the initial values of {@link Packet}. aoqi@0: * Some of those values go to strongly-typed fields, and others go to aoqi@0: * {@link #invocationProperties}, as they need to be retained in the reply message. aoqi@0: * aoqi@0: *
aoqi@0: * Similarly, {@link BindingProvider#getResponseContext() response context} aoqi@0: * is constructed from {@link Packet} (or rather it's just a view of {@link Packet}.) aoqi@0: * by using properties from {@link #invocationProperties}, aoqi@0: * modulo properties named explicitly in {@link #getHandlerScopePropertyNames(boolean)}. aoqi@0: * IOW, properties added to {@link #invocationProperties} aoqi@0: * are exposed to the response context by default. aoqi@0: * aoqi@0: * aoqi@0: * aoqi@0: *
aoqi@0: * This method should be only used to create a fresh {@link Packet}.
aoqi@0: * To create a {@link Packet} for a reply, use {@link #createResponse(Message)}.
aoqi@0: *
aoqi@0: * @param request
aoqi@0: * The request {@link Message}. Can be null.
aoqi@0: */
aoqi@0: public Packet(Message request) {
aoqi@0: this();
aoqi@0: this.message = request;
aoqi@0: if (message != null) message.setMessageMedadata(this);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Creates an empty {@link Packet} that doesn't have any {@link Message}.
aoqi@0: */
aoqi@0: public Packet() {
aoqi@0: this.invocationProperties = new HashMap
aoqi@0: * This is a requirement from the security team.
aoqi@0: */
aoqi@0: // TODO: expose this as a property
aoqi@0: public boolean wasTransportSecure;
aoqi@0:
aoqi@0: /**
aoqi@0: * Inbound transport headers are captured in a transport neutral way.
aoqi@0: * Transports are expected to fill this data after creating a Packet.
aoqi@0: *
aoqi@0: * {@link SOAPMessage#getMimeHeaders()} would return these headers.
aoqi@0: */
aoqi@0: public static final String INBOUND_TRANSPORT_HEADERS = "com.sun.xml.internal.ws.api.message.packet.inbound.transport.headers";
aoqi@0:
aoqi@0: /**
aoqi@0: * Outbound transport headers are captured in a transport neutral way.
aoqi@0: *
aoqi@0: *
aoqi@0: * Transports may choose to ignore certain headers that interfere with
aoqi@0: * its correct operation, such as
aoqi@0: * Content-Type and Content-Length.
aoqi@0: */
aoqi@0: public static final String OUTBOUND_TRANSPORT_HEADERS = "com.sun.xml.internal.ws.api.message.packet.outbound.transport.headers";
aoqi@0:
aoqi@0: /**
aoqi@0: *
aoqi@0: */
aoqi@0: public static final String HA_INFO = "com.sun.xml.internal.ws.api.message.packet.hainfo";
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * This property holds the snapshot of HandlerConfiguration
aoqi@0: * at the time of invocation.
aoqi@0: * This property is used by MUPipe and HandlerPipe implementations.
aoqi@0: */
aoqi@0: @Property(BindingProviderProperties.JAXWS_HANDLER_CONFIG)
aoqi@0: public HandlerConfiguration handlerConfig;
aoqi@0:
aoqi@0: /**
aoqi@0: * If a message originates from a proxy stub that implements
aoqi@0: * a port interface, this field is set to point to that object.
aoqi@0: *
aoqi@0: * TODO: who's using this property?
aoqi@0: */
aoqi@0: @Property(BindingProviderProperties.JAXWS_CLIENT_HANDLE_PROPERTY)
aoqi@0: public BindingProvider proxy;
aoqi@0:
aoqi@0: /**
aoqi@0: * Determines if the governing {@link Adapter} or {@link com.sun.xml.internal.ws.api.pipe.Fiber.CompletionCallback}
aoqi@0: * will handle delivering response messages targeted at non-anonymous endpoint
aoqi@0: * addresses. Prior to the introduction of this flag
aoqi@0: * the {@link WsaServerTube} would deliver non-anonymous responses.
aoqi@0: */
aoqi@0: public boolean isAdapterDeliversNonAnonymousResponse;
aoqi@0:
aoqi@0: /**
aoqi@0: * During invocation of a client Stub or Dispatch a Packet is
aoqi@0: * created then the Stub's RequestContext is copied into the
aoqi@0: * Packet. On certain internal cases the Packet is created
aoqi@0: * *before* the invocation. In those cases we want the contents
aoqi@0: * of the Packet to take precedence when ever any key/value pairs
aoqi@0: * collide : if the Packet contains a value for a key use it,
aoqi@0: * otherwise copy as usual from Stub.
aoqi@0: */
aoqi@0: public boolean packetTakesPriorityOverRequestContext = false;
aoqi@0:
aoqi@0: /**
aoqi@0: * The endpoint address to which this message is sent to.
aoqi@0: *
aoqi@0: *
aoqi@0: * The JAX-WS spec allows this to be changed for each message,
aoqi@0: * so it's designed to be a property.
aoqi@0: *
aoqi@0: *
aoqi@0: * Must not be null for a request message on the client. Otherwise
aoqi@0: * it's null.
aoqi@0: */
aoqi@0: public EndpointAddress endpointAddress;
aoqi@0:
aoqi@0: /**
aoqi@0: * @deprecated
aoqi@0: * The programatic acccess should be done via
aoqi@0: * {@link #endpointAddress}. This is for JAX-WS client applications
aoqi@0: * that access this property via {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}.
aoqi@0: */
aoqi@0: @Property(BindingProvider.ENDPOINT_ADDRESS_PROPERTY)
aoqi@0: public String getEndPointAddressString() {
aoqi@0: if (endpointAddress == null) {
aoqi@0: return null;
aoqi@0: } else {
aoqi@0: return endpointAddress.toString();
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: public void setEndPointAddressString(String s) {
aoqi@0: if (s == null) {
aoqi@0: this.endpointAddress = null;
aoqi@0: } else {
aoqi@0: this.endpointAddress = EndpointAddress.create(s);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * The value of {@link ContentNegotiation#PROPERTY}
aoqi@0: * property.
aoqi@0: *
aoqi@0: * Headers which have attribute wsa:IsReferenceParameter="true"
aoqi@0: * This is not cached as one may reset the Message.
aoqi@0: *
aoqi@0: */
aoqi@0: @Property(MessageContext.REFERENCE_PARAMETERS)
aoqi@0: public
aoqi@0: @NotNull
aoqi@0: List The representation shall be that specified by the HTTP Accept
aoqi@0: * request-header field.
aoqi@0: *
aoqi@0: * The list of content types will be obtained from the transport
aoqi@0: * meta-data of a inbound message in a request/response message exchange.
aoqi@0: * Hence this property will be set by the service-side transport pipe.
aoqi@0: */
aoqi@0: public String acceptableMimeTypes;
aoqi@0:
aoqi@0: /**
aoqi@0: * When non-null, this object is consulted to
aoqi@0: * implement {@link WebServiceContext} methods
aoqi@0: * exposed to the user application.
aoqi@0: *
aoqi@0: * Used only on the server side.
aoqi@0: *
aoqi@0: *
aoqi@0: * This property is set from the parameter
aoqi@0: * of {@link WSEndpoint.PipeHead#process}.
aoqi@0: */
aoqi@0: public WebServiceContextDelegate webServiceContextDelegate;
aoqi@0:
aoqi@0: /**
aoqi@0: * Used only on the server side so that the transport
aoqi@0: * can close the connection early.
aoqi@0: *
aoqi@0: *
aoqi@0: * This field can be null. While a message is being processed,
aoqi@0: * this field can be set explicitly to null, to prevent
aoqi@0: * future pipes from closing a transport (see {@link #keepTransportBackChannelOpen()})
aoqi@0: *
aoqi@0: *
aoqi@0: * This property is set from the parameter
aoqi@0: * of {@link WSEndpoint.PipeHead#process}.
aoqi@0: */
aoqi@0: public
aoqi@0: @Nullable
aoqi@0: TransportBackChannel transportBackChannel;
aoqi@0:
aoqi@0: /**
aoqi@0: * Keeps the transport back channel open (by seeting {@link #transportBackChannel} to null.)
aoqi@0: *
aoqi@0: * @return
aoqi@0: * The previous value of {@link #transportBackChannel}.
aoqi@0: */
aoqi@0: public TransportBackChannel keepTransportBackChannelOpen() {
aoqi@0: TransportBackChannel r = transportBackChannel;
aoqi@0: transportBackChannel = null;
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * The governing owner of this packet. On the service-side this is the {@link Adapter} and on the client it is the {@link Stub}.
aoqi@0: *
aoqi@0: */
aoqi@0: public Component component;
aoqi@0:
aoqi@0: /**
aoqi@0: * The governing {@link WSEndpoint} in which this message is floating.
aoqi@0: *
aoqi@0: *
aoqi@0: * This property is set if and only if this is on the server side.
aoqi@0: */
aoqi@0: @Property(JAXWSProperties.WSENDPOINT)
aoqi@0: public WSEndpoint endpoint;
aoqi@0:
aoqi@0: /**
aoqi@0: * The value of the SOAPAction header associated with the message.
aoqi@0: *
aoqi@0: *
aoqi@0: * For outgoing messages, the transport may sends out this value.
aoqi@0: * If this field is null, the transport may choose to send ""
aoqi@0: * (quoted empty string.)
aoqi@0: *
aoqi@0: * For incoming messages, the transport will set this field.
aoqi@0: * If the incoming message did not contain the SOAPAction header,
aoqi@0: * the transport sets this field to null.
aoqi@0: *
aoqi@0: *
aoqi@0: * If the value is non-null, it must be always in the quoted form.
aoqi@0: * The value can be null.
aoqi@0: *
aoqi@0: *
aoqi@0: * Note that the way the transport sends this value out depends on
aoqi@0: * transport and SOAP version.
aoqi@0: *
aoqi@0: * This property is used on the client-side for
aoqi@0: * outbound messages, so that a pipeline
aoqi@0: * can communicate to the terminal (or intermediate) {@link Tube}s
aoqi@0: * about this knowledge.
aoqi@0: *
aoqi@0: *
aoqi@0: * This property MUST NOT be used by 2-way transports
aoqi@0: * that have the transport back channel. Those transports
aoqi@0: * must always check a reply coming through the transport back
aoqi@0: * channel regardless of this value, and act accordingly.
aoqi@0: * (This is because the expectation of the client and
aoqi@0: * that of the server can be different, for example because
aoqi@0: * of a bug in user's configuration.)
aoqi@0: *
aoqi@0: *
aoqi@0: * This property is for one-way transports, and more
aoqi@0: * specifically for the coordinator that correlates sent requests
aoqi@0: * and incoming replies, to decide whether to block
aoqi@0: * until a response is received.
aoqi@0: *
aoqi@0: *
aoqi@0: * Also note that this property is related to
aoqi@0: * {@link WSDLOperation#isOneWay()} but not the same thing.
aoqi@0: * In fact in general, they are completely orthogonal.
aoqi@0: *
aoqi@0: * For example, the calling application can choose to invoke
aoqi@0: * {@link Dispatch#invoke(Object)} or {@link Dispatch#invokeOneWay(Object)}
aoqi@0: * with an operation (which determines the value of this property),
aoqi@0: * regardless of whether WSDL actually says it's one way or not.
aoqi@0: * So these two booleans can take any combinations.
aoqi@0: *
aoqi@0: *
aoqi@0: *
aoqi@0: * When this property is {@link Boolean#FALSE}, it means that
aoqi@0: * the pipeline does not expect a reply from a server (and therefore
aoqi@0: * the correlator should not block for a reply message
aoqi@0: * -- if such a reply does arrive, it can be just ignored.)
aoqi@0: *
aoqi@0: *
aoqi@0: * When this property is {@link Boolean#TRUE}, it means that
aoqi@0: * the pipeline expects a reply from a server (and therefore
aoqi@0: * the correlator should block to see if a reply message is received,
aoqi@0: *
aoqi@0: *
aoqi@0: * This property is always set to {@link Boolean#TRUE} or
aoqi@0: * {@link Boolean#FALSE} when used on the request message
aoqi@0: * on the client side.
aoqi@0: * No other {@link Boolean} instances are allowed.
aoqi@0: *
aoqi@0: *
aoqi@0: * In all other situations, this property is null.
aoqi@0: *
aoqi@0: */
aoqi@0: @Property(BindingProviderProperties.ONE_WAY_OPERATION)
aoqi@0: public Boolean expectReply;
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * This property will be removed in a near future.
aoqi@0: *
aoqi@0: *
aoqi@0: * A part of what this flag represented moved to
aoqi@0: * {@link #expectReply} and the other part was moved
aoqi@0: * to {@link Message#isOneWay(WSDLPort)}. Please update
aoqi@0: * your code soon, or risk breaking your build!!
aoqi@0: */
aoqi@0: @Deprecated
aoqi@0: public Boolean isOneWay;
aoqi@0:
aoqi@0: /**
aoqi@0: * Indicates whether is invoking a synchronous pattern. If true, no
aoqi@0: * async client programming model (e.g. AsyncResponse or AsyncHandler)
aoqi@0: * were used to make the request that created this packet.
aoqi@0: */
aoqi@0: public Boolean isSynchronousMEP;
aoqi@0:
aoqi@0: /**
aoqi@0: * Indicates whether a non-null AsyncHandler was given at the point of
aoqi@0: * making the request that created this packet. This flag can be used
aoqi@0: * by Tube implementations to decide how to react when isSynchronousMEP
aoqi@0: * is false. If true, the client gave a non-null AsyncHandler instance
aoqi@0: * at the point of request, and will be expecting a response on that
aoqi@0: * handler when this request has been processed.
aoqi@0: */
aoqi@0: public Boolean nonNullAsyncHandlerGiven;
aoqi@0:
aoqi@0: /**
aoqi@0: * USE-CASE:
aoqi@0: * WS-AT is enabled, but there is no WSDL available.
aoqi@0: * If Packet.isRequestReplyMEP() is Boolean.TRUE then WS-AT should
aoqi@0: * add the TX context.
aoqi@0: *
aoqi@0: * This value is exposed to users via facades at higher abstraction layers.
aoqi@0: * The user should NEVER use Packet directly.
aoqi@0: * This value should ONLY be set by users.
aoqi@0: */
aoqi@0: private Boolean isRequestReplyMEP;
aoqi@0: public Boolean isRequestReplyMEP() { return isRequestReplyMEP; }
aoqi@0: public void setRequestReplyMEP(final Boolean x) { isRequestReplyMEP = x; }
aoqi@0:
aoqi@0: /**
aoqi@0: * Lazily created set of handler-scope property names.
aoqi@0: *
aoqi@0: *
aoqi@0: * We expect that this is only used when handlers are present
aoqi@0: * and they explicitly set some handler-scope values.
aoqi@0: *
aoqi@0: * @see #getHandlerScopePropertyNames(boolean)
aoqi@0: */
aoqi@0: private Set
aoqi@0: * These properties are copied from a request to a response.
aoqi@0: * This is where we keep properties that are set by handlers.
aoqi@0: *
aoqi@0: *
aoqi@0: * See class javadoc for more discussion.
aoqi@0: *
aoqi@0: * @see #getHandlerScopePropertyNames(boolean)
aoqi@0: */
aoqi@0: public final Map
aoqi@0: * These properties will not be exposed to the response context.
aoqi@0: * Consequently, if a {@link Tube} wishes to hide a property
aoqi@0: * to {@link ResponseContext}, it needs to add the property name
aoqi@0: * to this set.
aoqi@0: *
aoqi@0: * @param readOnly
aoqi@0: * Return true if the caller only intends to read the value of this set.
aoqi@0: * Internally, the {@link Set} is allocated lazily, and this flag helps
aoqi@0: * optimizing the strategy.
aoqi@0: *
aoqi@0: * @return
aoqi@0: * always non-null, possibly empty set that stores property names.
aoqi@0: */
aoqi@0: public final Set
aoqi@0: * When a {@link Packet} for a reply is created, some properties need to be
aoqi@0: * copied over from a request to a response, and this method handles it correctly.
aoqi@0: *
aoqi@0: * @deprecated
aoqi@0: * Use createClientResponse(Message) for client side and
aoqi@0: * createServerResponse(Message, String) for server side response
aoqi@0: * creation.
aoqi@0: *
aoqi@0: * @param msg
aoqi@0: * The {@link Message} that represents a reply. Can be null.
aoqi@0: */
aoqi@0: @Deprecated
aoqi@0: public Packet createResponse(Message msg) {
aoqi@0: Packet response = new Packet(this);
aoqi@0: response.setMessage(msg);
aoqi@0: return response;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Creates a response {@link Packet} from a request packet ({@code this}).
aoqi@0: *
aoqi@0: *
aoqi@0: * When a {@link Packet} for a reply is created, some properties need to be
aoqi@0: * copied over from a request to a response, and this method handles it correctly.
aoqi@0: *
aoqi@0: * @param msg
aoqi@0: * The {@link Message} that represents a reply. Can be null.
aoqi@0: */
aoqi@0: public Packet createClientResponse(Message msg) {
aoqi@0: Packet response = new Packet(this);
aoqi@0: response.setMessage(msg);
aoqi@0: finishCreateRelateClientResponse(response);
aoqi@0: return response;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * For use cases that start with an existing Packet.
aoqi@0: */
aoqi@0: public Packet relateClientResponse(final Packet response) {
aoqi@0: response.relatePackets(this, true);
aoqi@0: finishCreateRelateClientResponse(response);
aoqi@0: return response;
aoqi@0: }
aoqi@0:
aoqi@0: private void finishCreateRelateClientResponse(final Packet response) {
aoqi@0: response.soapAction = null; // de-initializing
aoqi@0: response.setState(State.ClientResponse);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Creates a server-side response {@link Packet} from a request
aoqi@0: * packet ({@code this}). If WS-Addressing is enabled, a default Action
aoqi@0: * Message Addressing Property is obtained using
aoqi@0: * This method should be called to create application response messages
aoqi@0: * since they are associated with a {@link WSBinding} and {@link WSDLPort}.
aoqi@0: * For creating protocol messages that require a non-default Action, use
aoqi@0: * {@link #createServerResponse(Message, com.sun.xml.internal.ws.api.addressing.AddressingVersion, com.sun.xml.internal.ws.api.SOAPVersion, String)}.
aoqi@0: *
aoqi@0: * @param responseMessage The {@link Message} that represents a reply. Can be null.
aoqi@0: * @param wsdlPort The response WSDL port.
aoqi@0: * @param binding The response Binding. Cannot be null.
aoqi@0: * @return response packet
aoqi@0: */
aoqi@0: public Packet createServerResponse(@Nullable Message responseMessage, @Nullable WSDLPort wsdlPort, @Nullable SEIModel seiModel, @NotNull WSBinding binding) {
aoqi@0: Packet r = createClientResponse(responseMessage);
aoqi@0: return relateServerResponse(r, wsdlPort, seiModel, binding);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Copy all properties from ({@code this}) packet into a input {@link Packet}
aoqi@0: * @param response packet
aoqi@0: */
aoqi@0: public void copyPropertiesTo(@Nullable Packet response){
aoqi@0: relatePackets(response, false);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * A common method to make members related between input packet and this packet
aoqi@0: *
aoqi@0: * @param packet
aoqi@0: * @param isCopy 'true' means copying all properties from input packet;
aoqi@0: * 'false' means copying all properties from this packet to input packet.
aoqi@0: */
aoqi@0: private void relatePackets(@Nullable Packet packet, boolean isCopy)
aoqi@0: {
aoqi@0: Packet request;
aoqi@0: Packet response;
aoqi@0:
aoqi@0: if (!isCopy) { //is relate
aoqi@0: request = this;
aoqi@0: response = packet;
aoqi@0:
aoqi@0: // processing specific properties
aoqi@0: response.soapAction = null;
aoqi@0: response.invocationProperties.putAll(request.invocationProperties);
aoqi@0: if (this.getState().equals(State.ServerRequest)) {
aoqi@0: response.setState(State.ServerResponse);
aoqi@0: }
aoqi@0: } else { //is copy constructor
aoqi@0: request = packet;
aoqi@0: response = this;
aoqi@0:
aoqi@0: // processing specific properties
aoqi@0: response.soapAction = request.soapAction;
aoqi@0: response.setState(request.getState());
aoqi@0: }
aoqi@0:
aoqi@0: request.copySatelliteInto(response);
aoqi@0: response.isAdapterDeliversNonAnonymousResponse = request.isAdapterDeliversNonAnonymousResponse;
aoqi@0: response.handlerConfig = request.handlerConfig;
aoqi@0: response.handlerScopePropertyNames = request.handlerScopePropertyNames;
aoqi@0: response.contentNegotiation = request.contentNegotiation;
aoqi@0: response.wasTransportSecure = request.wasTransportSecure;
aoqi@0: response.transportBackChannel = request.transportBackChannel;
aoqi@0: response.endpointAddress = request.endpointAddress;
aoqi@0: response.wsdlOperation = request.wsdlOperation;
aoqi@0: response.wsdlOperationMapping = request.wsdlOperationMapping;
aoqi@0: response.acceptableMimeTypes = request.acceptableMimeTypes;
aoqi@0: response.endpoint = request.endpoint;
aoqi@0: response.proxy = request.proxy;
aoqi@0: response.webServiceContextDelegate = request.webServiceContextDelegate;
aoqi@0: response.expectReply = request.expectReply;
aoqi@0: response.component = request.component;
aoqi@0: response.mtomAcceptable = request.mtomAcceptable;
aoqi@0: response.mtomRequest = request.mtomRequest;
aoqi@0: // copy other properties that need to be copied. is there any?
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: public Packet relateServerResponse(@Nullable Packet r, @Nullable WSDLPort wsdlPort, @Nullable SEIModel seiModel, @NotNull WSBinding binding) {
aoqi@0: relatePackets(r, false);
aoqi@0: r.setState(State.ServerResponse);
aoqi@0: AddressingVersion av = binding.getAddressingVersion();
aoqi@0: // populate WS-A headers only if WS-A is enabled
aoqi@0: if (av == null) {
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: if (getMessage() == null) {
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: //populate WS-A headers only if the request has addressing headers
aoqi@0: String inputAction = AddressingUtils.getAction(getMessage().getHeaders(), av, binding.getSOAPVersion());
aoqi@0: if (inputAction == null) {
aoqi@0: return r;
aoqi@0: }
aoqi@0: // if one-way, then dont populate any WS-A headers
aoqi@0: if (r.getMessage() == null || (wsdlPort != null && getMessage().isOneWay(wsdlPort))) {
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: // otherwise populate WS-Addressing headers
aoqi@0: populateAddressingHeaders(binding, r, wsdlPort, seiModel);
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Creates a server-side response {@link Packet} from a request
aoqi@0: * packet ({@code this}). If WS-Addressing is enabled,
aoqi@0: * This method should be called only for creating protocol response messages
aoqi@0: * that require a particular value of Action since they are not associated
aoqi@0: * with a {@link WSBinding} and {@link WSDLPort} but do know the {@link AddressingVersion}
aoqi@0: * and {@link SOAPVersion}.
aoqi@0: *
aoqi@0: * @param responseMessage The {@link Message} that represents a reply. Can be null.
aoqi@0: * @param addressingVersion The WS-Addressing version of the response message.
aoqi@0: * @param soapVersion The SOAP version of the response message.
aoqi@0: * @param action The response Action Message Addressing Property value.
aoqi@0: * @return response packet
aoqi@0: */
aoqi@0: public Packet createServerResponse(@Nullable Message responseMessage, @NotNull AddressingVersion addressingVersion, @NotNull SOAPVersion soapVersion, @NotNull String action) {
aoqi@0: Packet responsePacket = createClientResponse(responseMessage);
aoqi@0: responsePacket.setState(State.ServerResponse);
aoqi@0: // populate WS-A headers only if WS-A is enabled
aoqi@0: if (addressingVersion == null) {
aoqi@0: return responsePacket;
aoqi@0: }
aoqi@0: //populate WS-A headers only if the request has addressing headers
aoqi@0: String inputAction = AddressingUtils.getAction(this.getMessage().getHeaders(), addressingVersion, soapVersion);
aoqi@0: if (inputAction == null) {
aoqi@0: return responsePacket;
aoqi@0: }
aoqi@0:
aoqi@0: populateAddressingHeaders(responsePacket, addressingVersion, soapVersion, action, false);
aoqi@0: return responsePacket;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Overwrites the {@link Message} of the response packet ({@code this}) by the given {@link Message}.
aoqi@0: * Unlike {@link #setMessage(Message)}, fill in the addressing headers correctly, and this process
aoqi@0: * requires the access to the request packet.
aoqi@0: *
aoqi@0: *
aoqi@0: * This method is useful when the caller needs to swap a response message completely to a new one.
aoqi@0: *
aoqi@0: * @see #createServerResponse(Message, AddressingVersion, SOAPVersion, String)
aoqi@0: */
aoqi@0: public void setResponseMessage(@NotNull Packet request, @Nullable Message responseMessage, @NotNull AddressingVersion addressingVersion, @NotNull SOAPVersion soapVersion, @NotNull String action) {
aoqi@0: Packet temp = request.createServerResponse(responseMessage, addressingVersion, soapVersion, action);
aoqi@0: setMessage(temp.getMessage());
aoqi@0: }
aoqi@0:
aoqi@0: private void populateAddressingHeaders(Packet responsePacket, AddressingVersion av, SOAPVersion sv, String action, boolean mustUnderstand) {
aoqi@0: // populate WS-A headers only if WS-A is enabled
aoqi@0: if (av == null) return;
aoqi@0:
aoqi@0: // if one-way, then dont populate any WS-A headers
aoqi@0: if (responsePacket.getMessage() == null)
aoqi@0: return;
aoqi@0:
aoqi@0: MessageHeaders hl = responsePacket.getMessage().getHeaders();
aoqi@0:
aoqi@0: WsaPropertyBag wpb = getSatellite(WsaPropertyBag.class);
aoqi@0: Message msg = getMessage();
aoqi@0: // wsa:To
aoqi@0: WSEndpointReference replyTo = null;
aoqi@0: Header replyToFromRequestMsg = AddressingUtils.getFirstHeader(msg.getHeaders(), av.replyToTag, true, sv);
aoqi@0: Header replyToFromResponseMsg = hl.get(av.toTag, false);
aoqi@0: boolean replaceToTag = true;
aoqi@0: try{
aoqi@0: if (replyToFromRequestMsg != null){
aoqi@0: replyTo = replyToFromRequestMsg.readAsEPR(av);
aoqi@0: }
aoqi@0: if (replyToFromResponseMsg != null && replyTo == null) {
aoqi@0: replaceToTag = false;
aoqi@0: }
aoqi@0: } catch (XMLStreamException e) {
aoqi@0: throw new WebServiceException(AddressingMessages.REPLY_TO_CANNOT_PARSE(), e);
aoqi@0: }
aoqi@0: if (replyTo == null) {
aoqi@0: replyTo = AddressingUtils.getReplyTo(msg.getHeaders(), av, sv);
aoqi@0: }
aoqi@0:
aoqi@0: // wsa:Action, add if the message doesn't already contain it,
aoqi@0: // generally true for SEI case where there is SEIModel or WSDLModel
aoqi@0: // false for Provider with no wsdl, Expects User to set the coresponding header on the Message.
aoqi@0: if (AddressingUtils.getAction(responsePacket.getMessage().getHeaders(), av, sv) == null) {
aoqi@0: //wsa:Action header is not set in the message, so use the wsa:Action passed as the parameter.
aoqi@0: hl.add(new StringHeader(av.actionTag, action, sv, mustUnderstand));
aoqi@0: }
aoqi@0:
aoqi@0: // wsa:MessageID
aoqi@0: if (responsePacket.getMessage().getHeaders().get(av.messageIDTag, false) == null) {
aoqi@0: // if header doesn't exist, method getID creates a new random id
aoqi@0: String newID = Message.generateMessageID();
aoqi@0: hl.add(new StringHeader(av.messageIDTag, newID));
aoqi@0: }
aoqi@0:
aoqi@0: // wsa:RelatesTo
aoqi@0: String mid = null;
aoqi@0: if (wpb != null) {
aoqi@0: mid = wpb.getMessageID();
aoqi@0: }
aoqi@0: if (mid == null) {
aoqi@0: mid = AddressingUtils.getMessageID(msg.getHeaders(), av, sv);
aoqi@0: }
aoqi@0: if (mid != null) {
aoqi@0: hl.addOrReplace(new RelatesToHeader(av.relatesToTag, mid));
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: // populate reference parameters
aoqi@0: WSEndpointReference refpEPR = null;
aoqi@0: if (responsePacket.getMessage().isFault()) {
aoqi@0: // choose FaultTo
aoqi@0: if (wpb != null) {
aoqi@0: refpEPR = wpb.getFaultToFromRequest();
aoqi@0: }
aoqi@0: if (refpEPR == null) {
aoqi@0: refpEPR = AddressingUtils.getFaultTo(msg.getHeaders(), av, sv);
aoqi@0: }
aoqi@0: // if FaultTo is null, then use ReplyTo
aoqi@0: if (refpEPR == null) {
aoqi@0: refpEPR = replyTo;
aoqi@0: }
aoqi@0: } else {
aoqi@0: // choose ReplyTo
aoqi@0: refpEPR = replyTo;
aoqi@0: }
aoqi@0: if (replaceToTag && refpEPR != null) {
aoqi@0: hl.addOrReplace(new StringHeader(av.toTag, refpEPR.getAddress()));
aoqi@0: refpEPR.addReferenceParametersToList(hl);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void populateAddressingHeaders(WSBinding binding, Packet responsePacket, WSDLPort wsdlPort, SEIModel seiModel) {
aoqi@0: AddressingVersion addressingVersion = binding.getAddressingVersion();
aoqi@0:
aoqi@0: if (addressingVersion == null) {
aoqi@0: return;
aoqi@0: }
aoqi@0:
aoqi@0: WsaTubeHelper wsaHelper = addressingVersion.getWsaHelper(wsdlPort, seiModel, binding);
aoqi@0: String action = responsePacket.getMessage().isFault() ?
aoqi@0: wsaHelper.getFaultAction(this, responsePacket) :
aoqi@0: wsaHelper.getOutputAction(this);
aoqi@0: if (action == null) {
aoqi@0: LOGGER.info("WSA headers are not added as value for wsa:Action cannot be resolved for this message");
aoqi@0: return;
aoqi@0: }
aoqi@0: populateAddressingHeaders(responsePacket, addressingVersion, binding.getSOAPVersion(), action, AddressingVersion.isRequired(binding));
aoqi@0: }
aoqi@0:
aoqi@0: public String toShortString() {
aoqi@0: return super.toString();
aoqi@0: }
aoqi@0:
aoqi@0: // For use only in a debugger
aoqi@0: @Override
aoqi@0: public String toString() {
aoqi@0: StringBuilder buf = new StringBuilder();
aoqi@0: buf.append(super.toString());
aoqi@0: String content;
aoqi@0: try {
aoqi@0: Message msg = getMessage();
aoqi@0: if (msg != null) {
aoqi@0: ByteArrayOutputStream baos = new ByteArrayOutputStream();
aoqi@0: XMLStreamWriter xmlWriter = XMLStreamWriterFactory.create(baos, "UTF-8");
aoqi@0: msg.copy().writeTo(xmlWriter);
aoqi@0: xmlWriter.flush();
aoqi@0: xmlWriter.close();
aoqi@0: baos.flush();
aoqi@0: XMLStreamWriterFactory.recycle(xmlWriter);
aoqi@0:
aoqi@0: byte[] bytes = baos.toByteArray();
aoqi@0: //message = Messages.create(XMLStreamReaderFactory.create(null, new ByteArrayInputStream(bytes), "UTF-8", true));
aoqi@0: content = new String(bytes, "UTF-8");
aoqi@0: } else {
aoqi@0: content = "wsdlPort
{@link WSDLPort}
aoqi@0: * and binding
{@link WSBinding}.
aoqi@0: * action
aoqi@0: * is used as Action Message Addressing Property.
aoqi@0: *