UnsupportedMediaException -> how do you get the actual response?
Asked Answered
S

1

2

I'm calling a remote web service and am occasionally getting the following error:-

Error caught: com.sun.xml.internal.ws.server.UnsupportedMediaException: Unsupported Content-Type: text/plain;charset=ISO-8859-1 Supported ones are: [text/xml]

Does anyone know how to get the actual message that was returned by the server? It sounds like it might be text or a web page but I'm unable to get it.

I can catch the UnsupportedMediaException but I don't know what to do to extract the actual response. Here's the code:-

    val selectedDate = exchange.`in`.getHeader("selectedDate").toString()
    val accountNumberMinor = exchange.`in`.getHeader("accountNumberMinor").toString()
    val accountNumberMajor = exchange.`in`.getHeader("accountNumberMajor").toString()
    val accountIdentifier = if (accountNumberMinor.trim() != "") accountNumberMinor else accountNumberMajor
    val effectiveDate = SimpleDateFormat("yyyy-MM-dd").parse(selectedDate)

    val response = webRequest.getResponse(accountIdentifier, selectedDate)

    val result = response.result as FixedIncomeCurrencyForwardAccountV10Result

Thanks,

Adam

Sideways answered 21/9, 2018 at 14:53 Comment(4)
What are you using to get response??Zachar
I'm using a Java api that was generated using the remote web service wsdl using Apache CXF wsdl2javaSideways
I mean you should show us your code you're trying to use so only then we can give you the answer what's wrong with it)Zachar
Edited with codeSideways
V
1

An HTML page is usually a server error yes. Probably a static service page (like 404 or 5xx). It could even be an error in your request that should be returned as a SOAPFault, but is not implemented as such by the specific server.

Sometimes the server does communicate a valid SOAP (Fault) message, but the content type header is just wrong. In that case you're better off rewriting the Content-Type from the response with a proxy server. See for references on this subject:

So, what can you do to view the HTML content?

With JAX-WS you can enable all HTTP web service traffic to be logged to System.out with the following vm options:

-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=TRUE
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=TRUE
-Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=TRUE
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=TRUE
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold=999999

See for references:

Now, this will dump all http requests and responses, but you might only be interested in the ones where you don't get soap/xml.

So, what else can you do?

You could set these options programmatically and re-send the request when you catch the UnsupportedMediaException. But in the time this takes the error might have disappeared. Note that these properties are cached, so setting them needs to go through com.sun.xml.ws.transport.http.client.HttpTransportPipe#setDump(Boolean)

If you're willing to switch to the JAX-WS runtime, you could also create your own com.sun.xml.ws.api.pipe.TransportTubeFactory since jaxws-rt can load custom instances of this factory. I have successfully created my own TransportTubeFactory that uses a custom com.sun.xml.ws.transport.http.client.HttpTransportPipe (by extending it and overriding processRequest) that reads the http response from the com.sun.xml.ws.api.pipe.Codec upon catching the UnsupportedMediaException. By wrapping the Codec you can store the input stream on the decode method call.

This runtime is nearly identical to the internal runtime, and should be fully compatible. This may also work with the internal classes from the Java runtime, but since those are located in RT.jar it's difficult to depend on it and build your project. So i would advice switching to the external JAX-WS runtime.

What you then do with the input stream is up to you (which is the body of the HTTP response at the moment of catching the UnsupportedMediaException). Note that you can also rewrite most* content type headers in code with this codec wrapper.

See for reference how to add your own implementation of this factory via META-INF/services here:

In short:

  • Create a file in META-INF/services called com.sun.xml.ws.api.pipe.TransportTubeFactory
  • The contents of this file should be a single line with the full class name of your custom factory, for example my.soap.transport.MyTransportTubeFactory

Note; if you're using the classes from the Java runtime instead of jaxws-rt, use com.sun.xml.internal.ws as the package for everything in this post that references com.sun.xml.ws.

*Note: newer versions of this runtime (jaxws-rt-2.3.x or jre 8+ internal) throw a different exception on text/html responses. Sadly before calling Codec.decode. So in that case you would have to copy more code into your custom HttpTransportPipe. text/plain currently still works though.

Some snippets of my code:

public class TransportTubeFactoryImpl extends TransportTubeFactory {

    @Override
    public Tube doCreate(ClientTubeAssemblerContext context) {
        String scheme = context.getAddress().getURI().getScheme();
        if (scheme != null) {
            if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
                CodecWrapper codec = new CodecWrapper(context.getCodec());
                return new HttpTransportPipeImpl(codec, context.getBinding());
            }
        }

        throw new WebServiceException("Unsupported endpoint address: " + context.getAddress());
    }
}
public class CodecWrapper implements Codec {

    private Codec wrapped;

    public CodecWrapper(Codec wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public void decode(InputStream in, String contentType, Packet response) throws IOException {
        copyInputStream(in); // todo: implement this
        wrapped.decode(in, contentType, response);
    }
}
public class HttpTransportPipeImpl extends HttpTransportPipe {

    private CodecWrapper codec;

    public HttpTransportPipeImpl(CodecWrapper codec, WSBinding binding) {
        super(codec, binding);
        this.codec = codec;
    }

    @Override
    public NextAction processRequest(Packet request) {
        try {
            return super.processRequest(request);
        } catch (UnsupportedMediaException ex) {
            // todo: here you can access the stored data from the codec wrapper
        }
    }
}

I have also created a complete working demonstration of this principle on my github: https://github.com/s-lindenau/SoapContentTypeDemo

If you still have the option to switch to a completely different client library, you could also check Apache CXF:

Venusberg answered 4/4, 2022 at 9:2 Comment(1)
Great and thorough write up!Chan

© 2022 - 2024 — McMap. All rights reserved.