How to intercept Apache CXF Servlet exceptions - java

I have developed a REST service using Apache CXF and notice that if I send invalid characters in the URL, the CXF servlet throws back the following exception before it gets to my code:
Servlet failed with Exception
java.lang.IllegalArgumentException
at java.net.URI.create(URI.java:841)
at org.apache.cxf.transport.servlet.BaseUrlHelper.getBaseURL(BaseUrlHelper.java:49)
at org.apache.cxf.transport.servlet.ServletController.getBaseURL(ServletController.java:73)
at org.apache.cxf.transport.servlet.ServletController.updateDestination(ServletController.java:82)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:162)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:137)
at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:158)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:239)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:164)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:215)
This results in a 500 status code being returned to the client.
I would ideally like to intercept this exception and return a 400 Bad Request status code to the client but I am unable to work out how to do this.
Any help much appreciated!
Many thanks

The way I did this is by means of an interceptor.
If an error has occurred during initial processing of a request, a framework response (and of course status code) will be put on the message pipe.
So you could for example write a interceptor on "marshall" phase, check if there already is a response and rewrite to your own response.
#Override
public void handleMessage(Message message) throws Fault {
if (message.getExchange().get(Response.class) != null) {
//build your response and put it on the exchange
message.getExchange().put(Response.class, yourResponse);
}

Related

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.

Exception handling - gracefully exit

I've a REST service which in turn consumes a SOAP WebService. Incase if i get an exception while calling the SOAP WS, what kind of exception to be thrown from REST service ? Will it be RuntimeException with http status code 500 ? Or should i gracefully exit by giving a proper error message and code as response to the caller, if so, what would be the http status code in this case ?
If you get an exception from the SOAP service, I would say that returning an HTTP 500 Internal Server Error is the only legal response to give to the caller of the REST service. Anything else would be misleading.

Getting the HTTP status code from the SOAP response

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();

Jersey not showing response in case it was an error

I have the following problem...
I'm testing a service that return HTTP responses on GET requests.
My problem is that I would like to view the response even if it was an HTTP 500 / 404 or whatever response.
I would like to view that. But I can't because it throws an exception and that's it.
Is there a way to view a jersey response even if it was an error response?
My code is like this:
webResource = client.resource(url);
response = webResource.queryParams(alertParams)
.header("x-token", token).get(String.class);
So when get receives an error response from the service I wont be able to view that although the response is something like this:
{
"errCode" : "ERR002",
"errMsg" : "",
"techErrMsg" : "LoginFailureGeneric"
}
Which is a 400 Bad Request.
Thanks very much for all the help!!
This is where you need to spend some time with the docs... WebRequest#get(Class) will throw an exception when you get an HTTP error status if you are trying to parse the response as anything other than ClientResponse.
So all you need to do is change the .get(String.class) -> .get(ClientResponse.class) and you can pull the entity itself (and the status, and everything else) off of the ClientResponse object sans exceptions.

Categories

Resources