Spring: Response time - java

I have a SOAP webservice(spring ws)and need to save the response time of every request that I receive. I could create a servlet filter that measures time difference between HTTP Request and HTTP Response. But, I need to log the response time together with some values that I read from soap request envelope, and since at that time request is raw and needs to be unmarshalled, that's an expensive and redundant operation to unmarshall for every request.
So is there a way to calculate it using SpringWS? like in a payloadInterceptor?

Yes, implementing an EndpointInterceptor is the best fit for this task, as it gives you access to the SOAP messages through the MessageContext. See the Reference Documenation.

I think you can use two tools.
AspectJ with its annotation #Before and #AfterReturning. The pointcut could be the method that receives the request (#WebMethod).
#Before("call([package, class and method that receives the request]")
public void before(JoinPoint joinPoint) throws Throwable {
...
}
#AfterReturning(pointcut = "call([package, class and method that receives the request])", returning = "result")
public void after(JoinPoint joinPoint, Object result) throws Throwable {
...
}
The JoinPoint object has the information of the method's parameters
Override the method handleMessage of a custom class that implements the SOAPHandler class. This method will be executed every time a SOAP request is received.
I hope this can give you ideas to resolve your problem.

Related

Spring ResponseErrorHandler without parsing objects

I am using SpringBoot with RestTemplate to communicate with another application. However, I am unable to change its API and this external service always Returns 200OK return code.
By Default, we have ResponseErrorHandler that reacts to all 4xx and 5xx response codes but in my case when there is an exception I get 200OK with one JSON field errors.
I have created a custom error handler and bundled it into my rest template by using:
restTemplate.errorHandler(new MyCustomErrorHandler());
I have also overrided hasError() method but inside I have to parse this object to check whether it contains fields with errors...
Is this a good approach for error handling? Should I parse response twice? I seek for the clean solution for such problems but I want to avoid parsing message twice every time I use external service
Error handler will only be invoked if an error state is returned, 200 is not an error state so it is not handled.
You can change this behavior by overriding hasError method ResponseErrorHandler to check for error message or any indication for error.
public class MyCustomErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse httpResponse)
throws IOException {
//TODO check your criteria for error
}

CXF-Proxy Client and #Suspended AsyncResponse

I recently learned that with JAX-RS 2.0 long running service endpoints can make use of the #Suspended annotation and AsyncResponse to free resources for incoming requests while the actual work is done in the background. All client examples - at least the ones I found so far - are either calling such endpoints directly (plain http-call) or make use of the JAX-RS client API. However I was not able to figure out how to use this with the proxy-based API.
Given a REST endpoint that uses #Suspended:
public interface HeavyLiftingService {
#GET
#Path("/heavylifting")
public void heavyLifting(#Suspended final AsyncResponse aResponse);
}
its implementation using Spring:
#Component
public class HeavyLiftingServiceImpl implements HeavyLiftingService {
#Override
#Async
public void heavyLifting(#Suspended final AsyncResponse aResponse) {
final Result result = doHeavyLifting();
aResponse.resume(result);
}
}
And a proxy-based client, that wants to obtain the result:
HeavyLiftingService proxy = JAXRSClientFactory.create("https://some-server.xyz", HeavyLiftingService.class);
proxy.heavyLifting(null); // what to put in here?
Result result = null; // how can I get the result?
Obviously there are two problems:
What do I need to provide to the heavyLifting method as value for the AsyncResponse parameter?
How can I get the result as the return type of methods using #Suspended has to be void?
And another question is how exceptions in the service method are handled. Will an exception automatically resume the response and return a corresponding error status?

Jersey client with null put method

I am working on a Jersey service client for one of my services and am having trouble determining the best way to pass a null entity through the client's put. On the service side of things this is my endpoint:
#PUT
#Path("/rule/disable/key/{key}")
#Produces(MediaType.APPLICATION_JSON)
public Response disableRuleByKey(#PathParam("key") String key)
throws Exception {
try {
DAL.getWriter().disableRuleByKey(key);
return Response.ok().build();
} catch (BlahException bla) {
throw de;
}
Basically all the method does in the backend is flip a toggle for other parts of the application to use. I'm not sure if put is the correct call to use here (but this was written by a teammate). I know it doesn't even have a JSON payload.
Anyways, on the client side I have this generic putItem() code for all of my clients to use via extends:
public static <T> boolean putItem(Client client, String uri, T item)
throws InterruptedException,
ExecutionException {
Invocation putConfig = client.target(uri).request()
.buildPut(Entity.entity(item, MediaType.APPLICATION_JSON));
Future<Response> asyncResponse = putConfig.submit();
Response response = asyncResponse.get();
return response.getStatus() == Status.OK.getStatusCode();
}
This PUTs into the database fine with a JSON payload, but since the method above doesn't specifically have a payload I was wondering what the best course of action would be. Would modifying the Invocation's .buildPut() to have null in it be okay since I am not passing in a payload.
I am open to modifying the endpoint too but this is what I currently have and can't figure out the best way to send this value to the backend. Should I just modify the endpoint to consume a JSON object rather than passing the key as a #PathParam?
When replacing the state of a resource with a PUT request, you should send the new representation in the request payload.
Have a look the the RFC 7231, the current reference for semantics and content in HTTP/1.1:
4.3.4. PUT
The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. [...]

Resteasy delegate route to another route

I was wondering if there was a way to delegate to another route in RestEasy.
I.e., something along the lines of, begin in a method inside an RS:
#Path("/api")
public class Foo {
#POST
#Path("/foo")
public Response foo() {
return RestEasy.delegate("GET", "/api/bar");
}
}
Where delegate would return the exact same response as if I had made an HTTP GET request to api/bar, that is, will fall through the proper RS that handles that route, ideally refilling all the necessary request information (headers, params, payload).
I don't want an HTTP redirect as I want it to be transparent to the api user.
I see from the docs/source that org.jboss.resteasy.spi.HttpRequest interface you have access to has a forward method.
It takes a string which would be the path to your other endpoint, but it doesn't let you change the method type (post to a get). But then again neither does the RequestDispatcher forward method you have access to. You aren't allowed to modify the request or the response.
See here:
So really all you can do is directly call your service method or, use an HTTP client to call other REST endpoint inside foo and then stream that back to the client.

RestEasy custom action on every request

I would like to invoke some custom method when a REST call has finished, looking up annotations on the originating method and the generated response.
I know you can use the PostProcessInterceptor or MessageBodyWriterInterceptor for this task but they do not get invoked in case of an exception.
My current solution is such that every method throws a special exception which is then handled by a custom ExceptionMapper, but there I have no information about the original request and where it came from.
Is there a global wide handler you can bind to in order to get the information about the original request in case of an exception?
And yes I know about this question: RestEasy Post Process Interceptor chain not traversed when response created by ExceptionMapper
To answer my own question.
One can inject the original request into the ExceptionMapper and react or perform a custom action accordingly.
#Provider
public class MyExceptionMapper implements ExceptionMapper<Throwable> {
#Context
private HttpServletRequest request;
#Override
public Response toResponse(Throwable exception)
{
// trigger event
triggerOnExceptionEvent(request, exception);
}
...
}

Categories