I have complex #RestController method, something like this:
#PostMapping("{id}")
#PreAuthorize("hasRole('ADMIN')")
#Transactional
public Response handleRequest(#PathVariable("id") long id, #RequestBody #Valid Request request) {
return service.handleRequest(id, request);
}
Our request handling is quite slow so we want to check how much time is spent on particular request handling tasks. Unfortunately lot of things are done outside of my method, like:
deserializing request
validating
permission checks
starting and ending transaction
serializing response
Is there way to simply measure all those parts? Maybe set of loggers that receive trace messages so I can pull timestamps at the end of each step?
The only way I see to do it now is change that method to accept HttpServletRequest and HttpServletResponse and do those parts inside method body. But that way I will lose lot of Spring Boot benefits.
you can also check a tuto for adding a custom metrics for actuator, but it seems a little bit complicate (but you'll you have to code your own metrics bean and inject it in your code, override objectMapper for mapping, etc...
)
or maybe activate logging info on jackson,spring-security, javax.validation for checking the time in the log for each operation, but not very precise
what you exactly need is Java Thread Profiler which will tell you what is exactly going wrong and for it you can use any APM Tools and my favourite is GLOWROOT .which I have used in the similar scenarios to measure the performance of APIs and identify the slow traces which will clearly tell you which method is taking time and you can see the entire trace starting from method call to all the methods called inside and even identify slow queries if there are any . Hope this helps
Ths site: https://glowroot.org/
example trace :
https://demo.glowroot.org/transaction/thread-profile?transaction-type=Web&transaction-name=%2Fhot-sauces
There is no need to change the method to expect HttpServletRequest. You can use AspectJ
Using it, you can collect the time spent on each method and that analyze the data from it.
Create a methodTiming annotarion
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MethodTiming {
}
In your Request, create a map that will keep all the methods and the time it took them:
public class Request {
private Map<String, Long> methodTimings = new TreeMap<String, Long>();
public void addMethodTiming(String classAndMethodName, long executionTimeMillis) {
Long value = methodTimings.get(classAndMethodName);
if (value != null) {
executionTimeMillis += value;
}
methodTimings.put(classAndMethodName, executionTimeMillis);
}
}
Than, create the Aspect class that will handle it:
#Aspect
#Component
public class MethodTimingAspect {
private static final String DOT = ".";
#Around("#annotation(MethodTiming)")
public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = null;
StopWatch watch = new StopWatch();
try {
watch.start();
result = joinPoint.proceed();
} finally {
watch.stop();
long executionTime = watch.getLastTaskTimeMillis();
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
String classAndMethodName = className + DOT + methodName;
Object[] methodArgs = joinPoint.getArgs();
if (methodArgs != null) {
for (Object arg : methodArgs) {
if (arg instanceof Request) {
// inject time back into Request
Request request = (Request) arg;
request.addMethodTiming(classAndMethodName, executionTime);
break;
}
}
}
}
return result;
}
Finally, simply add the #MethodTiming on the methods you wish measure:
#MethodTiming
public Request handleRequest(Request request) {
// handle the Request
return request
}
Your request object will than have after the process something like
"methodTimings": {
"RequestService.handleRequest": 2610,
"AnotherRequestService.anotherMethod": 1351
}
Related
I am implementing Custom logging in Jetty11 and getting the null pointer exception for the below code while fetching anything from the request object.
I know the reason but do not the solution to it. The reason is: calling the request object methods before setting it.
I know, there should be another way to do it.
My use case is to set the int and string attributes to the logger. In this example, I am calling request.getMethod() but I also have to call other methods as well
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
public class JettyCustomLogger extends CustomRequestLog
{
private static Request request;
#Override
public void log(Request request, Response response)
{
this.request = request;
super.log(request, response);
}
public JettyCustomLogger(Writer writer, String logStr)
{
super(writer, setCustomAttributesToLog(logStr));
}
private static String setCustomAttributesToLog(String logStr)
{
String method = request.getMethod();
StringBuilder logBuffer = new StringBuilder(logStr);
logBuffer.append(method);
logBuffer.append("Ashish");
logBuffer.append(" ");
logBuffer.append("Goyanka");
logBuffer.append(" ");
logBuffer.append("absgdh");
logBuffer.append(" ");
return logBuffer.toString();
}
}
Note: this code works fine if I don't call request object methods.
Update: the reason to create setCustomAttributesToLog() is that I need to fetch the string parameters from other methods on runtime but here, I have given hardcoded string for code readability
The log(request, response) method is called for every request response pair, this method is expected to produce some formatted log string and then give it to the RequestLogWriter. You should not be using setCustomAttributesToLog() in the constructor as there is no request yet, and you should not be storing the request as a static field. All your logic to convert produce the log string based on the request must be done in the log() method.
For your current implementation, there should be no reason you cannot use the standard CustomRequestLog. If you read the javadoc on this class there is the concept of the format string. The format string is generic and takes % codes to be replaced with information from an actual request/response.
To get what you're trying to achieve here you could do something like
CustomRequestLog requestLog = new CustomRequestLog(writer, "%m Ashnish Goyanka absgdh ")
Then for each request the %m will be replaced with the request method, then this string will be given to the writer.
If you do not wish to use the format string functionality of CustomRequestLog I would suggest you implement the RequestLog interface directly instead of extending CustomRequestLog.
From reading around, it appears generally bad practice and a sign of code smell when you have to spy on a method that is used by the current method you're unit testing.
For example, I have this method that I'm unit testing:
public MyResponseObject doStuff(MyRequestObject obj) {
WebTarget tar = getServiceClient().target(obj.toString());
Response res = tar.path(someURI).request().post(somejson);
if(response.getStatus() == 200) {
String jsonResp = response.readEntity(String.class);
return convertToObj(jsonResp);
}
}
One way I'm attempting to solve the above is by
Extracting the first two lines (WebTarget, Response) to its own method that returns a Response object.
Creating a mock of Response and stubbing readEntity to return 200 and readEntity to return "OK"
Here's the result:
public MyResponseObject doStuff(MyRequestObject obj) {
Response res = sendRequest(obj.toString());
if(response.getStatus() == 200) {
String jsonResp = response.readEntity(String.class);
return convertToObj(jsonResp);
}
}
//extracted method
public Response sendRequest(String json){
WebTarget tar = getServiceClient().target(someUrl);
return res = tar.path(someURI).request().post(somejson);
}
//My unit test
//sut is the system under test, setup elsewhere
public void testDoStuff() {
MyRequestObject request = ...;
Response respMock = mock(Response.class);
when(respMock.getStatus()).thenReturn(200);
when(respoMock.readEntity()).thenReturn("OK");
MyClass spy = spy(sut);
Mockito.doReturn(respMock).when(spy).sendRequest(requestString);
MyResponseObject response = spy.doStuff(request);
assertEquals(response.toString(),expectedResp);
}
If I do not stub this out, it attempts to do a real HTTP request and returns an invalid URL error because I'm not supplying a real one - I believe this is what I want because I want my unit tests to be independent of some external system.
Is there a better way I should be going about my unit testing?
Yes creating a spy of the class you're testing is bad practice, break the code you're mocking out into another class and mock that, i.e:
public class MyClass {
private final MySender sender;
public MyClass() {
this(new DefaultSender());
}
public MyClass(MySender sender) {
this.sender = sender;
}
public MyResponseObject doStuff(MyRequestObject obj) {
Response res = sender.sendRequest(obj.toString());
if (response.getStatus() == 200) {
String jsonResp = response.readEntity(String.class);
return convertToObj(jsonResp);
}
}
public interface MySender {
Response sendRequest(String json);
}
private static class DefaultSender implements MySender {
public Response sendRequest(String json) {
WebTarget tar = getServiceClient().target(someUrl);
return res = tar.path(someURI).request().post(somejson);
}
}
}
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
private MyClass testSubject;
#Mock
private MySender sender;
#Mock
private Response response;
#Test
public void testDoStuff() {
String expectedResp = ...;
MyRequestObject request = ...;
MyResponseObject response = testSubject.doStuff(request);
assertEquals(response.toString(),expectedResp);
}
#Before
public void setup() {
testSubject = new MyClass(sender);
when(sender.sendRequest(anyString()).thenReturn(response);
when(response.getStatus()).thenReturn(200);
when(response.readEntity()).thenReturn("OK");
}
}
Spying an object you are testing has been communicated to me as bad practice, but these warnings come with little to no explanation backing it up. I'm sure, like anything else, it can definitely be abused.
What I have noticed when testing a method that calls another method of the object being tested, that it is better if you can knock out testing of both methods at the same time. While you could mock the second method, to make the testing of the first method easier, you'll still need to go back at some point and test the second method. Personally I support mocking the second method if it leads to cleaner test code. Basically its an option that the universe gives you, and it should not be ruled out for all cases.
In your scenario, my personal preference would be to mock the WebTarget object, versus creating a second internal method. The main reason for this is that you will have to go back and test the second method anyways, so might as well tackle it now. But if you find that your code could be cleaner by separating those first two lines into their own method (or class) because it is re-usable code that is used multiples times, then of course split it out into its own method. In this case, the architecture of the code itself (and not of the testing requirements) dictate the code structure.
When mocking the WebTarget, in this case, it involves with dealing with the builder methods, e.g. .path(someURI).request().post(somejson), which each have to get mocked accordingly. So that's a bit of a pain. If I was doing this, I would probably use integration testing over unit testing if possible. That is to say, have the server that is being contacted up and available so that I can test against it. In our test environment, all the servers remain up so that we can do leverage more integration tests over unit-tests. As the environment grows, this might not be an option, but right now it is and it leads to cleaner integration-tests that knock out a lot of code coverage in less tests.
In our web application we have a lot of REST services. Suddenly it found out that we need to modify one object inside of each request before we go on.
So let's say we have n different controllers with REST services. In each controller, before we call the service from next layer, we need to modify an object inside the request.
The question is how to achieve this without providing hundreds of changes inside the controllers... Is there any simple way to do this?
UPDATE:
#RestController
public class OrderController {
#Autowired
private OrderService orderService;
#RequestMapping(path = "/order", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public OrderResponse getOrderData(#RequestHeader HttpHeaders httpHeaders,
#RequestBody OrderDataRequest orderDataRequest) {
// Use here interceptor to modify the object Details
// (inside OrderDataRequest) before below call:
OrderResponse resp = orderService.getOrderData(orderDataRequest);
return resp;
}
#RequestMapping(path = "/cancel/{orderId}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public boolean cancelOrder(#RequestHeader HttpHeaders httpHeaders,
#RequestBody Details details, #PathVariable Integer orderId) {
// Use here interceptor to modify object Details before below call:
return orderService.cancelOrder(details, orderId);
}
}
In each controller I need to modift the object Details, which as you can see could be inside another object like in the first example or exist alone like in the second option.
You can use Spring AOP to achieve this. Another option using traditional Filters.
You should consider writing an interceptor, that would allow you to do what you want .
You could also use AOP to do this.. though, I think it's quite over-complicated, especially when such a solution already exists through interceptors!
EDIT :
A few other links :
EDIT 2 :
Follow the "before advice" from mykong.com example, then go that way to edit your specific object according to its class (for exemple) :
package com.your.company;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class HijackBeforeMethod implements MethodBeforeAdvice
{
#Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
for(Object arg : args) {
if(com.your.company.OrderDataRequest.class.isAssignableFrom(arg.getClass())) {
// update you object here
}
}
}
}
- Get response body
- Json To Java
In order to optimize sql request, I've made a service that aggregate other services consumptions to avoid unecessary calls.
(Some pages of my webapp are called millions times by day, so I want to reuse the results of database queries as many times as possible on each request)
The solution I create is this one :
My service has #RequestScope instead of default scope (Singleton)
In MyService
#Service
#RequestScope
public MyService {
private int param;
#Autowired
private OtherService otherService;
#Autowired
private OtherService2 otherService2;
private List<Elements> elements;
private List<OtherElements> otherElements;
public void init(int param) {
this.param = param;
}
public List<Elements> getElements() {
if(this.elements == null) {
//Init elements
this.elements = otherService.getElements(param);
}
return this.elements;
}
public List<OtherElements> getOtherElements() {
if(this.otherElements == null) {
//Init otherElements
this.otherElements = otherService2.getOtherElements(param);
}
return this.otherElements;
}
public String getMainTextPres() {
//Need to use lElements;
List<Elements> elts = this.getElements();
....
return myString;
}
public String getSecondTextPres() {
//Need to use lElements;
List<Elements> elts = this.getElements();
//Also Need to use lElements;
List<OtherElements> otherElts = this.getOtherElements();
....
return myString;
}
}
In my controller :
public class myController {
#Autowired MyService myService;
#RequestMapping...
public ModelAndView myFunction(int param) {
myService.init(param);
String mainTextPres = myService.getMainTextPres();
String secondTextPres = myService.getSecondTextPres();
}
#OtherRequestMapping...
public ModelAndView myFunction(int param) {
myService.init(param);
String secondTextPres = myService.getSecondTextPres();
}
}
Of course, I've simplified my example, because myService use lots of other elements, and i protect the initialization of his members attributes
This method has the advantage of doing lazy loading of the attributes only when I need them.
If somewhere in my project (in same or other controller) I only need the SecondTextPres, then calling "getSecondTextPres" will initialize both lists which is not the case in my example beacuse the first list has been initialized when "getMainTextPres" was called.
My question are :
What do you think of this way of doing things ?
May I have performance issues because I instantiate my service on each request ?
Thanks a lot !
Julien
I think that your idea is not going to fly. I you call the same or different controller this is will be different request - in that case new bean will be created (elements and other elements are empty again).
Have you been thinking about caching? Spring has nice support where you can define cache expiration, etc
It's not quite clear to me what exactly you want to optimise instantiating Service in request scope? If you are bothered about memory foot print, you could easily measure it by JMX or VisualVM.
On the other hand, you could make all Service calls pure, i.e. depending on function parameters and (ofc) database state only and instantiate the Service with default scope as Singleton.
This decision will save you reasonable amount of resources as you will not instantiate possible large object graph on each call and will not require GC to clean the thing after Request is done.
The rule of thumb is to think why exactly you need the specific Class instantiated on every call and if it doesn't keep any state specific to call, make it Singleton.
Speaking about lazy loading, it always helps to think about worst case repeated like 100 times. Will it really save you something comparing to be loaded once and for the whole Container lifetime.
Is it somehow possible to define cache control level for each resource?
Is there some annotation i could put on top of each Resource class so to specify cache control in a custom way?
To solve this issue I ended up defining an #Cache aspect. There are a couple of caveats that I ran into:
The cached method needs to return a Response object (not POJOs) so that the aspect can modify the headers.
If you want to use ETags then the class containing the method (or the method arguments themselves) needs to expose the #Request so that the aspect can inspect the incoming tag.
Make sure that your method arguments combined with your method name will in fact create unique keys. Another catch here is response type. The client may issue two requests with identical parameters but different response types.
You could easily add options to the aspect to control max-age and other parameters on a per-method basis.
Here's my invoke method:
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if (Response.class != invocation.getMethod().getReturnType()) {
logger.warning(String.format("%s is #Cachable but does not return a Response object", invocation.getMethod()));
return invocation.proceed();
}
Optional<Request> request = getRequest(invocation);
if (!request.isPresent()) {
logger.warning(String.format("%s is #Cachable but does not declare a public Request field named \"request\"", invocation.getMethod()));
return invocation.proceed();
}
Object response = super.invoke(invocation);
EntityTag responseTag = JaxRsUtil.getEntityTag(response);
Response.ResponseBuilder builder = request.get().evaluatePreconditions(responseTag);
if (null != builder) {
logger.finer("Found matching etag: " + responseTag);
return builder.build();
}
CacheControl cc = new CacheControl();
cc.setMaxAge(maxAge);
builder = Response.fromResponse((Response)response).tag(responseTag).cacheControl(cc);
return builder.build();
}