dev@jax-ws.java.net

Re: SOAP Action and other heretical ideas <was> [Fwd: CVS update [rearch-2005]: /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/api/pipe/, /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/, /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/transport/http/client/]

From: Vivek Pandey <Vivek.Pandey_at_Sun.COM>
Date: Thu, 23 Feb 2006 07:15:31 -0800

Paul Sandoz wrote:
> Hi Vivek,
>
> I wonder if there is a better way to handle SOAP action more cleanly
> rather than exposing SOAP 1.1 HTTP transport binding semantics to the
> Encoder
Other option that I suggested on other/similar email thread to return
ITerable<String> containing the HTTP headers by the Encoder. Since the
original idea of encode() to return was ContentType, just to handle SOAP
1.1 SOAPAction this was introduced. The transports that dont care about
soap 1.1 wouldnt even call ContentType.getSOAPaction().
> and SOAP 1.2 HTTP transport binding semantics to the implementations?
>
Currently MtomEncoder is well tied to SOAP. I will make it generic
enough so that other protocols can reuse it.

> The same encoders may be used with other transports (JMS, SOAP/TCP,
> BEEP, SMTP, XMPP) and new encoders may be used with the same transport
> (FI). Some encoders may not encode SOAP messages.
>
Can they not avoid calling ContentType.getSOAPAction(). The same issue
exists with MEssage.getHeaderList(), Headers are SOAP concepts too. I am
not saying this is something we should practice in general but there is
not any other cleaner way I could think of.
> It is tricky though, especially when having to support MTOM!
>
>
why?
> Putting my heretical hat on i am becoming more and more convinced that
> the transport pipe cannot be separated from the binding of the
> protocol to the transport e.g. the SOAP 1.1 or SOAP 1.2 transport
> binding or the MTOM transport binding.
>
> I think a general problem is that content type is meant to be
> meta-data but it is leaking into the encoding layer (a bit like what
> prefixes do for qnames in content). In this respect i am not sure that
> Encoder/Decoder should actually be using the content type string at
> all for the actual encode/decode process. And this goes to the heart
> of the argument of using facades up front as well which tend to
> encourage transport level binding semantics to be pushed to the
> encoding/decoding layer.
>
Not sure if I folllowed you here. Content-Type is somthing that encoders
can reliably determine. Same way it helps decoders tell soap version.
> At the moment i still think things are too restrictive for pluggable
> transports, sorry :-(
>
> Transport pipes can be responsible for instantiating the
> encoder/decoders they require from factories obtained from the
> pipeline constructor (since the constructor is in best position to
> know what implementations to use e.g. SAAJ, stream, JAXB etc). The
> constructor can also use the binding to determine what
> encoders/decoders are allowed (easy way to switch of FI).
>
> The transport pipe is in the best position to determine how to use the
> Encoders/Decoders based on binding and runtime information.
>
> Encoders/Decoders can operate on Message and take parameters and
> return parameters (e.g. to support MTOM). Thus Encoder/Decoder is
> cleanly separated from the content type leaking and transports. This
> does create more work for a transport pipe, but all transport binding
> stuff is kept localized and is not spread between the transport pipe
> and Encoder/Decoder. Helper classes can be defined to aid the
> development of a transport pipe.
>
At least SOAP encoders/decoders work on the transport level header such
as Content-Type - for example to let charset to determine encoding to
decode the SOAP envelope..

> I will try and write a clearer proposal on this.
>
Ok It will help to see in terms of class/interfaces etc.


-vivek.
> Paul.
>
>
> ------------------------------------------------------------------------
>
> Subject:
> CVS update [rearch-2005]:
> /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/api/pipe/,
> /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/,
> /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/transport/http/client/
> From:
> vivekp_at_dev.java.net
> Date:
> Wed, 22 Feb 2006 23:40:40 +0000
> To:
> cvs_at_jax-ws-sources.dev.java.net
>
> To:
> cvs_at_jax-ws-sources.dev.java.net
>
>
> Tag: rearch-2005
> User: vivekp
> Date: 2006/02/22 15:40:40
>
> Log:
> Changes for SOAPAction support:
> - Encoder.encode() returns Content-Type
> - String getStaticContentType() changed to ContentType getStaticContentType(Packet)
> - HTTPTransportPipe writes out SOAPAction header if ContentType.getSOAPACtion is non-null.
>
> File Changes:
>
> Directory: /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/api/pipe/
> ===================================================================
>
> File [changed]: Encoder.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/api/pipe/Encoder.java?r1=1.1.2.4&r2=1.1.2.5
> Delta lines: +3 -3
> -------------------
> --- Encoder.java 7 Feb 2006 19:11:47 -0000 1.1.2.4
> +++ Encoder.java 22 Feb 2006 23:40:35 -0000 1.1.2.5
> @@ -74,7 +74,7 @@
> * null if the content-type may change from a {_at_link Message} to {_at_link Message}.
> * Otherwise return the static content type, like "application/xml".
> */
> - String getStaticContentType();
> + ContentType getStaticContentType(Packet packaet);
>
> /**
> * Encodes an XML infoset portion of the {_at_link Message}
> @@ -96,7 +96,7 @@
> * @throws IOException
> * if a {_at_link OutputStream} throws {_at_link IOException}.
> */
> - String encode( Packet packet, OutputStream out ) throws IOException;
> + ContentType encode( Packet packet, OutputStream out ) throws IOException;
>
> /**
> * The version of {_at_link #encode(Packet,OutputStream)}
> @@ -106,7 +106,7 @@
> * TODO: for the convenience of implementation, write
> * an adapter that wraps {_at_link WritableByteChannel} to {_at_link OutputStream}.
> */
> - String encode( Packet packet, WritableByteChannel buffer );
> + ContentType encode( Packet packet, WritableByteChannel buffer );
>
> /*
> * The following methods need to be documented and implemented.
>
> File [added]: ContentType.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/api/pipe/ContentType.java?rev=1.1.2.1&content-type=text/vnd.viewcvs-markup
> Added lines: 22
> ---------------
> package com.sun.xml.ws.api.pipe;
>
> /**
> * A Content-Type transport header that will be returned by {_at_link Encoder#encode(com.sun.xml.ws.api.message.Packet, java.io.OutputStream)}.
> * It will provide the Content-Type header and also take care of SOAP 1.1 SOAPAction header.
> *
> * @author Vivek Pandey
> */
> public interface ContentType {
> /**
> * Gives non-null Content-Type header value.
> */
> public String getContentType();
>
> /**
> * Gives SOAPAction transport header value. It will be non-null only for SOAP 1.1 messages. In other cases
> * it MUST be null. The SOAPAction transport header should be written out only when its non-null.
> *
> * @return It can be null, in that case SOAPAction header should be written.
> */
> public String getSOAPAction();
> }
>
> Directory: /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/
> =======================================================================
>
> File [changed]: EncoderFacade.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/EncoderFacade.java?r1=1.1.2.2&r2=1.1.2.3
> Delta lines: +5 -4
> -------------------
> --- EncoderFacade.java 15 Feb 2006 00:18:36 -0000 1.1.2.2
> +++ EncoderFacade.java 22 Feb 2006 23:40:36 -0000 1.1.2.3
> @@ -1,6 +1,7 @@
> package com.sun.xml.ws.sandbox.impl;
>
> import com.sun.xml.ws.api.pipe.Encoder;
> +import com.sun.xml.ws.api.pipe.ContentType;
> import com.sun.xml.ws.api.message.Packet;
> import com.sun.xml.ws.api.SOAPVersion;
>
> @@ -26,15 +27,15 @@
> this.binding = binding;
> }
>
> - public String getStaticContentType() {
> - return getEncoder().getStaticContentType();
> + public ContentType getStaticContentType(Packet packet) {
> + return getEncoder().getStaticContentType(packet);
> }
>
> - public String encode(Packet packet, OutputStream out) throws IOException {
> + public ContentType encode(Packet packet, OutputStream out) throws IOException {
> return getEncoder().encode(packet, out);
> }
>
> - public String encode(Packet packet, WritableByteChannel buffer) {
> + public ContentType encode(Packet packet, WritableByteChannel buffer) {
> return getEncoder().encode(packet, buffer);
> }
>
>
> File [changed]: MimeEncoder.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/MimeEncoder.java?r1=1.1.2.9&r2=1.1.2.10
> Delta lines: +6 -6
> -------------------
> --- MimeEncoder.java 2 Feb 2006 01:18:37 -0000 1.1.2.9
> +++ MimeEncoder.java 22 Feb 2006 23:40:36 -0000 1.1.2.10
> @@ -1,8 +1,8 @@
> package com.sun.xml.ws.sandbox.impl;
>
> -import com.sun.xml.messaging.saaj.packaging.mime.internet.ContentType;
> import com.sun.xml.messaging.saaj.packaging.mime.util.OutputUtil;
> import com.sun.xml.ws.api.pipe.Encoder;
> +import com.sun.xml.ws.api.pipe.ContentType;
> import com.sun.xml.ws.api.message.Attachment;
> import com.sun.xml.ws.api.message.Message;
> import com.sun.xml.ws.api.message.Packet;
> @@ -25,20 +25,20 @@
> /**
> * TODO: can we generate a content-id ? otherwise, buffering is required
> */
> - public String getStaticContentType() {
> + public ContentType getStaticContentType(Packet packet) {
> return null;
> }
>
> // TODO: preencode String literals to byte[] so that they don't have to
> // go through char[]->byte[] conversion at runtime.
>
> - public String encode(Packet packet, OutputStream out) throws IOException {
> + public ContentType encode(Packet packet, OutputStream out) throws IOException {
> Message msg = packet.getMessage();
>
> String primaryCid = null; // TODO
> String primaryCt = null; // TODO
>
> - ContentType contentType = new ContentType("multipart", "related", null);
> + com.sun.xml.messaging.saaj.packaging.mime.internet.ContentType contentType = new com.sun.xml.messaging.saaj.packaging.mime.internet.ContentType("multipart", "related", null);
> String boundary = UniqueValue.getUniqueBoundaryValue();
> contentType.setParameter("type", "text/xml"); // TODO
> contentType.setParameter("boundary", boundary);
> @@ -72,10 +72,10 @@
> OutputUtil.writeAsAscii(startBoundary, out); // write --boundary
> OutputUtil.writeAsAscii("--", out);
>
> - return "multipart/related";
> + return new ContentTypeImpl("multipart/related", null);
> }
>
> - public String encode(Packet packet, WritableByteChannel buffer) {
> + public ContentType encode(Packet packet, WritableByteChannel buffer) {
> //TODO: not yet implemented
> throw new UnsupportedOperationException();
> }
>
> File [changed]: MtomEncoder.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/MtomEncoder.java?r1=1.1.2.6&r2=1.1.2.7
> Delta lines: +36 -10
> ---------------------
> --- MtomEncoder.java 18 Feb 2006 02:25:22 -0000 1.1.2.6
> +++ MtomEncoder.java 22 Feb 2006 23:40:36 -0000 1.1.2.7
> @@ -5,6 +5,7 @@
> import com.sun.xml.ws.api.message.AttachmentSet;
> import com.sun.xml.ws.api.message.Attachment;
> import com.sun.xml.ws.api.pipe.Encoder;
> +import com.sun.xml.ws.api.pipe.ContentType;
> import com.sun.xml.ws.streaming.XMLStreamWriterFactory;
> import com.sun.xml.messaging.saaj.packaging.mime.util.OutputUtil;
>
> @@ -44,12 +45,23 @@
> private String messageContentType;
> private final String soapContentType;
> private XMLStreamWriter writer;
> + private final SOAPVersion version;
>
> //This is the mtom attachment stream, we should write it just after the root part for decoder
> private final List<ByteArrayOutputStream> mtomAttachmentStream = new ArrayList<ByteArrayOutputStream>();
>
> - public MtomEncoder(String contentType){
> - this.soapContentType = contentType;
> + MtomEncoder(SOAPVersion version){
> + this.version = version;
> + switch(version){
> + case SOAP_11:
> + this.soapContentType = "text/xml";
> + break;
> + case SOAP_12:
> + this.soapContentType = "application/soap+xml";
> + break;
> + default:
> + throw new AssertionError();
> + }
> createConteTypeHeader();
> this.soapXopContentType = xopContentType+";charset=utf-8;type=\""+soapContentType+"\"";
> }
> @@ -65,18 +77,32 @@
> *
> * @return A non-null content type for soap11 or soap 1.2 content type
> */
> - public String getStaticContentType() {
> - return messageContentType;
> + public ContentType getStaticContentType(Packet packet) {
> + return getContentType(packet);
> + }
> +
> + private ContentType getContentType(Packet packet){
> + switch(version){
> + case SOAP_11:
> + return new ContentTypeImpl(messageContentType, (packet.soapAction == null)?"":packet.soapAction);
> + case SOAP_12:
> + if(packet.soapAction != null){
> + messageContentType += ";action=\""+packet.soapAction+"\"";
> + }
> + return new ContentTypeImpl(messageContentType, null);
> + }
> + //never happens
> + return null;
> }
>
> private OutputStream writeMtomBinary(String contentType){
> throw new UnsupportedOperationException();
> }
>
> - public String encode(Packet packet, OutputStream out) throws IOException {
> + public ContentType encode(Packet packet, OutputStream out) throws IOException {
> //get the current boundary thaat will be reaturned from this method
> mtomAttachmentStream.clear();
> - String currBoundary = boundary;
> + ContentType contentType = getContentType(packet);
> this.writer = XMLStreamWriterFactory.createXMLStreamWriter(out);
> if(packet.getMessage() != null){
> try {
> @@ -116,7 +142,7 @@
> }
> //now create the boundary for next encode() call
> createConteTypeHeader();
> - return currBoundary;
> + return contentType;
> }
>
> private static final String XOP_LOCALNAME = "Include";
> @@ -172,7 +198,7 @@
> }
> }
>
> - public String encode(Packet packet, WritableByteChannel buffer) {
> + public ContentType encode(Packet packet, WritableByteChannel buffer) {
> throw new UnsupportedOperationException();
> }
>
> @@ -211,8 +237,8 @@
> return name + cid;
> }
>
> - public static final Encoder SOAP11 = new MtomEncoder("text/xml");
> - public static final Encoder SOAP12 = new MtomEncoder("application/soap+xml");
> + public static final Encoder SOAP11 = new MtomEncoder(SOAPVersion.SOAP_11);
> + public static final Encoder SOAP12 = new MtomEncoder(SOAPVersion.SOAP_12);
>
> public static Encoder get(SOAPVersion version) {
> if(version==null)
>
> File [changed]: TestEncoderImpl.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/TestEncoderImpl.java?r1=1.1.2.12&r2=1.1.2.13
> Delta lines: +14 -5
> --------------------
> --- TestEncoderImpl.java 2 Feb 2006 03:27:34 -0000 1.1.2.12
> +++ TestEncoderImpl.java 22 Feb 2006 23:40:37 -0000 1.1.2.13
> @@ -1,6 +1,7 @@
> package com.sun.xml.ws.sandbox.impl;
>
> import com.sun.xml.ws.api.pipe.Encoder;
> +import com.sun.xml.ws.api.pipe.ContentType;
> import com.sun.xml.ws.api.message.Packet;
> import com.sun.xml.ws.api.SOAPVersion;
> import com.sun.xml.ws.streaming.XMLStreamWriterFactory;
> @@ -25,11 +26,11 @@
> this.contentType = contentType;
> }
>
> - public String getStaticContentType() {
> - return contentType;
> + public ContentType getStaticContentType(Packet packet) {
> + return getContentType(packet.soapAction);
> }
>
> - public String encode(Packet packet, OutputStream out) {
> + public ContentType encode(Packet packet, OutputStream out) {
> if (packet.getMessage() != null) {
> XMLStreamWriter writer = XMLStreamWriterFactory.createXMLStreamWriter(out);
> try {
> @@ -38,10 +39,10 @@
> throw new WebServiceException(e);
> }
> }
> - return contentType;
> + return getContentType(packet.soapAction);
> }
>
> - public String encode(Packet packet, WritableByteChannel buffer) {
> + public ContentType encode(Packet packet, WritableByteChannel buffer) {
> //TODO: not yet implemented
> throw new UnsupportedOperationException();
> }
> @@ -65,5 +66,13 @@
> default:
> throw new AssertionError();
> }
> + }
> +
> + private ContentType getContentType(String soapAction){
> + if((soapAction != null) && contentType.equals("application/soap+xml"))
> + return new ContentTypeImpl(contentType + ";action=\""+soapAction+"\"", null);
> +
> + String action = (soapAction == null)?"":soapAction;
> + return new ContentTypeImpl(contentType, action);
> }
> }
>
> File [added]: ContentTypeImpl.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/sandbox/impl/ContentTypeImpl.java?rev=1.1.2.1&content-type=text/vnd.viewcvs-markup
> Added lines: 24
> ---------------
> package com.sun.xml.ws.sandbox.impl;
>
> import com.sun.xml.ws.api.pipe.ContentType;
>
> /**
> * @author Vivek Pandey
> */
> class ContentTypeImpl implements ContentType {
> private final String contentType;
> private final String soapAction;
>
> public ContentTypeImpl(String contentType, String soapAction) {
> this.contentType = contentType;
> this.soapAction = soapAction;
> }
>
> public String getContentType() {
> return contentType;
> }
>
> public String getSOAPAction() {
> return soapAction;
> }
> }
>
> Directory: /jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/transport/http/client/
> ================================================================================
>
> File [changed]: HttpTransportPipe.java
> Url: https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/transport/http/client/HttpTransportPipe.java?r1=1.1.2.33&r2=1.1.2.34
> Delta lines: +18 -5
> --------------------
> --- HttpTransportPipe.java 22 Feb 2006 20:33:22 -0000 1.1.2.33
> +++ HttpTransportPipe.java 22 Feb 2006 23:40:37 -0000 1.1.2.34
> @@ -24,6 +24,7 @@
> import com.sun.xml.ws.api.pipe.Encoder;
> import com.sun.xml.ws.api.pipe.Pipe;
> import com.sun.xml.ws.api.pipe.PipeCloner;
> +import com.sun.xml.ws.api.pipe.ContentType;
> import com.sun.xml.ws.api.WSBinding;
> import com.sun.xml.ws.spi.runtime.WSConnection;
> import com.sun.xml.ws.util.ByteArrayBuffer;
> @@ -71,19 +72,22 @@
>
> HttpClientTransport con = new HttpClientTransport(request,reqHeaders);
>
> - String ct = encoder.getStaticContentType();
> + ContentType ct = encoder.getStaticContentType(request);
> if (ct == null) {
> ByteArrayBuffer buf = new ByteArrayBuffer();
> ct = encoder.encode(request, buf);
> // data size is available, set it as Content-Length
> reqHeaders.put("Content-Length", Arrays.asList(""+buf.size()));
> - reqHeaders.put("Content-Type", Arrays.asList(ct));
> + reqHeaders.put("Content-Type", Arrays.asList(ct.getContentType()));
> + writeSOAPAction(reqHeaders, ct.getSOAPAction());
> buf.writeTo(con.getOutput());
> } else {
> // Set static Content-Type
> - reqHeaders.put("Content-Type", Arrays.asList(ct));
> + reqHeaders.put("Content-Type", Arrays.asList(ct.getContentType()));
> + writeSOAPAction(reqHeaders, ct.getSOAPAction());
> encoder.encode(request, con.getOutput());
> }
> +
> con.closeOutput();
>
> Map<String, List<String>> respHeaders = con.getHeaders();
> @@ -92,14 +96,23 @@
> || con.statusCode==WSConnection.ONEWAY) {
> return new Packet(null); // one way. no response given.
> }
> - ct = getContentType(respHeaders);
> + String contentType = getContentType(respHeaders);
> Packet reply = request.createResponse(null);
> - decoder.decode(con.getInput(), ct, reply);
> + decoder.decode(con.getInput(), contentType, reply);
> return reply;
> } catch(WebServiceException wex) {
> throw wex;
> } catch(Exception ex) {
> throw new WebServiceException(ex);
> + }
> + }
> +
> + /**
> + * write SOAPAction header if the soapAction parameter is non-null
> + */
> + private void writeSOAPAction(Map<String, List<String>> reqHeaders, String soapAction) {
> + if(soapAction != null){
> + reqHeaders.put("SOAPAction", Arrays.asList(soapAction));
> }
> }
>
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: cvs-unsubscribe_at_jax-ws-sources.dev.java.net
> For additional commands, e-mail: cvs-help_at_jax-ws-sources.dev.java.net
>
>
>
> ------------------------------------------------------------------------
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_jax-ws.dev.java.net
> For additional commands, e-mail: dev-help_at_jax-ws.dev.java.net
>