Getting the HTTP status code from the SOAP response - java

How can I get the HTTP status from the result of the SOAPConnection.call()?

Taken from W3C note on SOAP (Section 6.2)
SOAP HTTP follows the semantics of the HTTP Status codes for
communicating status information in HTTP. For example, a 2xx status
code indicates that the client's request including the SOAP component
was successfully received, understood, and accepted etc.
In case of a SOAP error while processing the request, the SOAP HTTP
server MUST issue an HTTP 500 "Internal Server Error" response and
include a SOAP message in the response containing a SOAP Fault element
(see section 4.4) indicating the SOAP processing error.
And from documentation on SOAPFault in the API
An element in the SOAPBody object that contains error and/or status
information. This information may relate to errors in the SOAPMessage
object or to problems that are not related to the content in the
message itself.
So, a possible answer could be
SoapMessage soapMessage = null;
soapMessage = MySOAPConnection.call(...);
soapMessage.getSOAPPart().getEnvelope().getBody().getFault().getFaultCode();
Some references which helped me create this answer are:
http://forums.devshed.com/java-help-9/java-httpstatus-code-59166.html
Apache Axis2 SAAP SoapConnectionImpl

The simple answer is you can't. Burrowing into the HttpSOAPConnection code, a local instance of an HttpURLConnection object is used to do the actual communication with the target service. This does get the httpResponse code but it more or less completely hides it from the caller. All you conclude is that if you don't get an exception but the returned SOAPMessage contains a SOAPFault, then the return code was HttpURLConnection.HTTP_INTERNAL_ERROR (i.e. 500). No exception and no SOAPFault means the return code was 200 to 206, all of which are "SUCCESS" - unfortunately the status entry from the HTTP headers in the HttpURLConnection object is explicitly not copied to the MIMEHeaders in the returned SOAPMessage ...
// Header field 0 is the status line so we skip it.
Anything else will raise an exception and the code will start after the open bracket in the message field of the exception and is probably three digits, it's hard to be precise because someone forgot the close bracket or any other separator before the message...
throw new SOAPExceptionImpl(
"Bad response: ("
+ responseCode
+ httpConnection.getResponseMessage());
For example:
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Bad response: (502internal error - server connection terminated
It's horrible relying on the formatting of a text message in an exception, but the response code isn't exposed anywhere else.

You can get access to the HTTP headers through the MessageContext interface.
http://docs.oracle.com/javaee/5/api/javax/xml/ws/handler/MessageContext.html
The most straight forward way is probably to implement a SOAPHandler which will give you access to the MessageContext:
http://docs.oracle.com/cd/E15051_01/wls/docs103/webserv_adv/handlers.html#wp222394
However, SOAP applications are generally not supposed to build the interaction on the HTTP status codes as those are transport specific.

Another alternative (java 8) :
public class HttpResponseHandler implements SOAPHandler<SOAPMessageContext> {
private Logger log = Logger.create(HttpResponseHandler.class);
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean outboundProperty = (boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); // Response
if(!outboundProperty) {
int status = (int)context.get(MessageContext.HTTP_RESPONSE_CODE);
log.debug("HTTP status code = " + status);
}
return true;
}
}
// Usage : building your service
List<Handler> soapHandlers = new ArrayList();
soapHandlers.add(new HttpResponseHandler());
URL wsdlDocumentLocation = this.getClass().getResource("some_url");
Service service = Service.create(wsdlDocumentLocation, new QName("namespace", "servicename"));
service.setHandlerResolver(new HandlerResolver() {
public List<Handler> getHandlerChain(PortInfo portInfo) {
return soapHandlers;
}
});
BindingProvider provider = (BindingProvider)service.getPort(new QName("namespace", "portname"), serviceInterface);
provider.getRequestContext().put("javax.xml.ws.service.endpoint.address", this.endpointAddress);
provider.getRequestContext().put("com.sun.xml.ws.connect.timeout", connectTimeout);
provider.getRequestContext().put("com.sun.xml.ws.request.timeout", requestTimeout);
return provider;

I do have similar requirement stated as this question, business analyst want to log every http response code related to every inbound and outbound soap calls.. My answer is valid for Apache CXF 2.7.5.
You can access http status code via MessageContext interface by the below code fragment in an implementation of javax.xml.ws.handler.soap.SoapHandler interface.
int status = (( javax.servlet.http.HttpServletResponse)messageContext.get("HTTP.RESPONSE")).getStatus();

Related

How to read body following an 101 response code?

From the docs,
immediately after sending the 101
(Switching Protocols) response, the server is expected to continue
responding to the original request as if it had received its
equivalent within the new protocol (i.e., the server still has an
outstanding request to satisfy after the protocol has been changed,
and is expected to do so without requiring the request to be
repeated).
If the Upgrade header field is received in a GET request
and the server decides to switch protocols, it first responds with a
101 (Switching Protocols) message in HTTP/1.1 and then immediately
follows that with the new protocol's equivalent of a response to a
GET on the target resource. This allows a connection to be upgraded
to protocols with the same semantics as HTTP without the latency cost
of an additional round trip.
I have made my interceptors(Using OkHttp) as follows
public class H2cUpgradeRequestInterceptor implements Interceptor {
private static final Log logger = LogFactory.getLog(H2cUpgradeRequestInterceptor.class);
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request upgradeRequest = request.newBuilder().addHeader("Connection", "Upgrade, HTTP2-Settings")
.addHeader("Upgrade", "h2c").addHeader("HTTP2-Settings", "AAMAAABkAARAAAAAAAIAAAAA").build();
Response upgradeResponse = chain.proceed(upgradeRequest);
if (upgradeResponse != null && upgradeResponse.code() == HttpStatus.SC_SWITCHING_PROTOCOLS) {
logger.debug("Switching Protocols success"); // Success. Got 101 in reply.
}
upgradeResponse.body(); // returns null
// I am clueless on how to read the response hereafter.
// As per the docs, the 101 status code reply will be followed by a data stream. How can I access this?
return upgradeResponse;
}
}
So basically, for a single request. I will receive 101 as response first if the upgrade is successful, then followed by another response as per the upgraded protocol(if my understanding is right?). Is there anyway to achieve this with OkHttp? Or, Any other client also would be helpful.
Thanks!

Unable to view reply in SOAP call

I'm currently working on a custom SOAP call to a specific domain beyond my control. I know the SOAP call fails but I cannot seem to grab the returned (wrong)value.
Right now I'm using the code below:
Document document = convertStringToDocument(this.MeldingString);
// System.out.println(document);
SOAPConnectionFactory myFct = SOAPConnectionFactory.newInstance();
MessageFactory myMsgFct = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SOAPMessage message = myMsgFct.createMessage();
SOAPConnection myCon = myFct.createConnection();
// Adding parts
SOAPPart mySPart = message.getSOAPPart();
SOAPEnvelope myEnvp = mySPart.getEnvelope();
// Escape the password for usage in header
String escpwd = StringEscapeUtils.escapeJava(this.Password);
// Header
MimeHeaders headers = message.getMimeHeaders();
headers.setHeader("Content-Type", "application/soap+xml;charset=UTF-8");
headers.setHeader("Authorization", this.Username + ":" + escpwd);
// Body
SOAPBody body = myEnvp.getBody();
body.addDocument(document);
// Sending
Core.getLogger("GetResultSOAPmsg").trace("Started");
URL endPt = new URL(
"URL-TO-MY-SERVICE");
System.setProperty("java.net.useSystemProxies", "true");
try {
SOAPMessage reply = myCon.call(message, endPt); "UTF-8");
}
catch(Exception e)
{
e.printStackTrace();
}
This yields the following error which is very common al throughout SO:
SEVERE: SAAJ0537: Invalid Content-Type. Could be an error message instead of a SOAP message
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:text/html. Is this an error message instead of a SOAP response?
Now I have read most of these topics already and they explain how this problem is usually solved (namespaces, escaping URL, etc.) but I cannot seem to figure out what is wrong in my case. This is a private service and the other side is unfortunately unable to assist me in this case. The error could be anything from wrong certificates to misspelling the URL.
Therefore I would like to actually SEE onscreen what the actual reply is that was received when making the call. This is going to help me (assuming it's something like a 503, 404 or other page). Regardless of what I do and where I set my breakpoints, there is no information on Reply. It makes somewhat sense since it was unable to create said object but the entire message seems to be discarded.
In what way will I be able to see what the actual reply was to my call before it is discarded?
I think there's a problem with your header
headers.setHeader("Content-Type", "application/soap+xml;charset=UTF-8");
try something like
headers.setHeader("Content-Type", "application/xml;charset=UTF-8");
or
headers.setHeader("Content-Type", "application/json;charset=UTF-8");
depending on the content type that the content type accepted by the service

Jersey http client

I'm using jersey http client to send requests to some remote API. I need to measure how much time does it take to send request to the server and wait until it gets processed and server returns me some status code. Is there a way how I can do it with jersey?
Here is my code of post method:
public Response post(String targetUrl, Entity entity)
{
return client.target(targetUrl)
.request()
.accept(MediaType.APPLICATION_JSON_TYPE)
.header(SERVER_AUTH, true)
.post(entity);
}
Actually, it was my fault. By default, this client is synchronized so it blocks thread until response is received. But my problem was that URL was incorrect and code immediately returned status 'Resouce not found.'

Jersey/JAX-RS Client throwing 400 Bad Request

I have a RESTful Java web service that I built using Jersey. The client for it defines a resource with the following method:
#Override
public String saveWidget(Widget widget) {
return webResource.path("user").type(MediaType.APPLICATION_JSON).entity(widget).post(String.class, Widget.class);
}
Then, a driver using this client:
public class Driver {
public static void main(String[] args) {
WidgetClient client;
WidgetClientBuilder builder = new WidgetClientBuilder();
client = builder.withUri("http://localhost:8080/myapi").build();
Widget w = getSomehow();
String widgetUri = client.getWidgetResource().saveWidget(w);
System.out.println("Widget was saved URI was returned: " + widgetUri);
}
}
When I run this I get:
Exception in thread "main" com.sun.jersey.api.client.UniformInterfaceException: POST http://localhost:8080/myapi/widget returned a response status of 400 Bad Request
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:688)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:570)
at com.my.myapi.WidgetResource.saveWidget(WidgetResource.java:27)
at com.my.myapi.Driver.main(Driver.java:32)
I know the service endpoint is valid because I can hit it from another (non-Java) web client without issues. This means that either my Widget instance is malformed or that there is something with my Java client method (saveWidget). I ruled out my w Widget being bad by serializing it into JSON, and then copying it into my non-Java web client and POSTing to the same endpoint (no issues arose). So this tells me I have the client method configured wrong. Any ideas?
This is regarding making a call POST call using Jersey client.
For jersey client, default client configuration uses ChunkedEncoding and gzip. This can be checked in request headers for POST call. Content length of payload (JSON String or any object mapper pojo) and request headers received by post call i.e. header name CONTENT-LENGTH, CONTENT-ENCODING. If there is difference, POST call might return 400 bad request. (Something like unable to process JSON). To solve this, you can disable ChunkedEncoding, gzip encoding. Code snippet for the same:
clientConfiguration.setChunkedEncodingEnabled(false);
clientConfiguration.setGzipEnabled(false);
Client client = (new JerseyClientBuilder(environment)).using(clientConfiguration).using(environment).build("HTTP_CLIENT");
WebTarget webTarget = client.target(endpoint);
Response response = webTarget.path(path).request(MediaType.APPLICATION_JSON).post(Entity.json(jsonString));
.post(String.class, Widget.class);
You appear to be posting a Class object, not a Widget object.

Java web service client generated in Netbeans - getting Http Status Code 307

I use Netbeans to generate web service client code, client-style JAX-WS, so i can invoke a web service API.
However, when I invoke the web service API, I get the exception:
com.sun.xml.internal.ws.client.ClientTransportException: The server sent HTTP status code 307: Temporary Redirect
Why do I get this? What is the workaround? I know the problem isn't with the web service itself, because I can get responses fine via soapUI and .Net.
Faced the same problem about a month ago.
Web service client classes were generated using Apache CXF and web service returned HTTP
status 307, which led to the same exception.
Invocation of the same web service method using soapUI with property Follow Redirects set to true was successful and returned needed data.
After googling awhile, it looked like there is no property to enable following redirects in the JAX-WS for this.
So, below is the code which is currently working, though I'm not sure it is compliant with any standards:
Supposing generated client classes looks like:
// generated service class
public class MyWebServiceClient extends javax.xml.ws.Service {
// ...
private final QName portName = "...";
// ...
public RetrieveMyObjects getRetrieveMyObjects() {
return super.getPort(portName, RetrieveMyObject.class);
}
// ...
}
// generated port interface
// annotations here
public interface RetrieveMyObjects {
// annotations here
List<MyObject> getAll();
}
Now, upon executing following code:
MyWebServiceClient wsClient = new MyWebServiceClient("wsdl/location/url/here.wsdl");
RetrieveMyObjectsPort retrieveMyObjectsPort = wsClient.getRetrieveMyObjects();
wsClient should return instance which is both instance of RetrieveMyObjects & javax.xml.ws.BindingProvider interfaces. It is not stated anywhere on the surface of JAX-WS, but it seems that a lot of code is based on that fact. One can re-assure him\herself by executing something like:
if(!(retrieveMyObjectsPort instanceof javax.xml.ws.BindingProvider)) {
throw new RuntimeException("retrieveMyObjectsPort is not instance of " + BindingProvider.class + ". Redirect following as well as authentication is not possible");
}
Now, when we are sure that retrieveMyObjectsPort is instance of javax.xml.ws.BindingProvider we can send plain HTTP POST request to it, simulating SOAP request (though it looks incredibly incorrect & ugly, but this works in my case and I didn't find anything better while googling) and check whether web service will send redirect status as a response:
// defined somewhere before
private static void checkRedirect(final Logger logger, final BindingProvider bindingProvider) {
try {
final URL url = new URL((String) bindingProvider.getRequestContext().get(ENDPOINT_ADDRESS_PROPERTY));
logger.trace("Checking WS redirect: sending plain POST request to {}", url);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/html; charset='UTF-8'");
connection.setDoOutput(true);
if(connection.getResponseCode() == 307) {
final String redirectToUrl = connection.getHeaderField("location");
logger.trace("Checking WS redirect: setting new endpoint url, plain POST request was redirected with status {} to {}", connection.getResponseCode(), redirectToUrl);
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, redirectToUrl);
}
} catch(final Exception e) {
logger.warn("Checking WS redirect: failed", e);
}
}
// somewhere at the application start
checkRedirect(logger, (BindingProvider) retrieveMyObjectsPort);
Now, what this method does is: it takes BindingProvider.ENDPOINT_ACCESS_PROPERTY of retrieveMyObjectsPort i.e. the url to which this port method will be sending SOAP requests and sends plain HTTP POST request as described above. Then it checks whether response status is 307 - Temporary Redirect (other statuses like 302 or 301 may also be included) and if it is, gets the URL to which web service is redirecting and sets new endpoint for the specified port.
In my case this checkRedirect method is called once for each web service port interface and then everything seems to work fine:
Redirect is checked on url like http://example.com:50678/restOfUrl
Web service redirects to url like https://example.com:43578/restOfUrl (please note that web service client authentication is present) - endpoint of a port is set to that url
Next web service requests executed via that port are successful
Disclaimer: I'm quite new to webservices and this is what I managed to achieve due to the lack of solutions for this questions, so please correct me if something is wrong here.
Hope this helps
Yes I know this post is old, but I've had similar errors, and thought maybe somebody would benefit from my solution.
the one that plagued me the most was:
com.sun.xml.ws.client.ClientTransportException: The server sent HTTP status code 200: OK
Which turns out to mean an incomplete response header. Apparently jax-ws does some kind of validation that includes validating the HTTP headers as well. And the server I was using was just sending an empty header.
It worked like a charm after adding 'application/soap+xml' to the Content-Type header.

Categories

Resources