I want to measure the time from the start of an incoming HTTP request and the application getting to a certain point. Both those points in time are located in different classes. How would I start and stop a timer from these different classes. I don't see a way to use 'named' timers from the MeterRegistry.
How would I go about this?
You can use AOP as below :
#Aspect
#Component
public class ControllerMonitor {
protected static final Logger LOGGER = LoggerFactory.getLogger(ControllerMonitor.class);
#Before("execution(public * com.demo.controller.*Controller.*(..))")
public void logBeforeAccess(JoinPoint joinPoint) {
if(joinPoint!=null){
String packageName = joinPoint.getSignature()!=null?joinPoint.getSignature().getDeclaringTypeName():"LOG-404";
LOGGER.info(". . .A request initiated from controller [" + packageName + "."+ getMethodSignature(joinPoint) + "]. . .");
}
}
#After("execution(public * com.demo.controller.*Controller.*(..))")
public void logAfterAccess(JoinPoint joinPoint) {
if(joinPoint!=null){
String packageName = joinPoint.getSignature()!=null?joinPoint.getSignature().getDeclaringTypeName():"LOG-404";
LOGGER.info(". . .Request from controller [" + packageName + "."+ getMethodSignature(joinPoint) + "] completed. . .");
}
}
#AfterThrowing(pointcut = "execution(public * com.demo.controller.*Controller.*(..))",throwing="exception")
public void logAfterThrowing(Exception exception){
LOGGER.error("Exception caught:"+ exception.getMessage());
}
private String getMethodSignature(JoinPoint joinPoint){
if(joinPoint!=null){
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
StringBuilder sb=new StringBuilder();
if(arguments!=null){
for (Object param: arguments) {
sb.append(param).append(",");
}
sb =(sb.length()>1)?sb.deleteCharAt(sb.length()-1):sb;
}
methodName = methodName+"("+new String(sb)+")";
return methodName;
}else{
return "LOG-405";
}
}
}
Use AOP …...No need to do changes on each class level. It will be one place config..
I want to log a message in the format MethodName invoked at date-time in the console whenever the method successfully completes.
I have defined a custom performance intwerceptor by extending the AbstarctMonitoringInterceptor. Then I am confused what to do next? I mean how to include it in xml or in my aspect class, so that it executes?
Here is my code:
public class MyPerformanceMonitorInterceptor extends AbstractMonitoringInterceptor {
public MyPerformanceMonitorInterceptor() {
}
public MyPerformanceMonitorInterceptor(boolean useDynamicLogger) {
setUseDynamicLogger(useDynamicLogger);
}
#Override
protected Object invokeUnderTrace(MethodInvocation mi, Log log) throws Throwable {
// TODO Auto-generated method stub
String name = createInvocationTraceName(mi);
long start = System.currentTimeMillis();
log.info("Method " + name + " execution started at:" + new Date());
try {
return mi.proceed();
}
finally {
long end = System.currentTimeMillis();
long time = end - start;
log.info("Method "+name+" execution lasted:"+time+" ms");
log.info("Method "+name+" execution ended at:"+new Date());
}
}
Logging is my aspect class. How to add this custom to my aspect class and get the invocation time?
i got a function that looks like this:
#GET
#Path("/execute/{scriptId}")
public String execute(#Context HttpServletRequest req, #PathParam("scriptId") Long scriptId) {
/* ... */
engine.eval(getSrc(req.getServletContext().getRealPath("js/boot.js")));
if (scriptId == 1L)
engine.eval(getSrc(req.getServletContext().getRealPath("js/test.js")));
else
engine.eval(getSrc(req.getServletContext().getRealPath("js/test2.js")));
/* that above, its the only place i need the req */
}
i call it from a html page...
execute 1
and it works fine...
now...i made a timer....and in the timer i need to call that function, but i have no idea how to get the httpservletrequest parameter for the function...
here is the code:
#Timeout
public void execute(Timer timer) {
Long scriptId = Long.parseLong(timer.getInfo().toString());
execute(/*here i need something*/, scriptId);
System.out.println("Timer Service : " + scriptId);
System.out.println("Current Time : " + new Date());
System.out.println("Next Timeout : " + timer.getNextTimeout());
System.out.println("Time Remaining : " + timer.getTimeRemaining());
System.out.println("____________________________________________");
}
so, basically, i need to call that function with the timer...
any ideas?
If your function doesn't need the HttpServletRequest (i.e. it doesn't need to call methods on the HttpServletRequest) then you can extract your existing code into an implementation method that does not depend on an HttpServletRequest and in your execute method call that implementation:
#GET
#Path("/execute/{scriptId}")
public String execute(#Context HttpServletRequest req, #PathParam("scriptId") Long scriptId) {
return executeImpl(scriptId);
}
public String executeImpl(Long scriptId) {
...// your current implementation
}
And then your timer can also call that method:
#Timeout
public void execute(Timer timer) {
Long scriptId = Long.parseLong(timer.getInfo().toString());
executeImpl(scriptId);
System.out.println("Timer Service : " + scriptId);
System.out.println("Current Time : " + new Date());
System.out.println("Next Timeout : " + timer.getNextTimeout());
System.out.println("Time Remaining : " + timer.getTimeRemaining());
System.out.println("____________________________________________");
}
Sure, it's just an interface which you can implement.
Of course implementing it to do something useful may not be trivial, depending on what you're doing with the request in the other method.
Getting a ready implementation of an HttpServletRequest from some 3rd party library that implements the JEE standard might help, but may well be overkill.
#Retryable doesn't seem to be working on 2nd level of methods as in sphRemoteCall below. I see that a proxy is created but it is never retried on failures.
Once I moved #Retryable to the 1st level of methods like getSubscriberAccount, it's started working.
Example below:
#Service
public class SphIptvClient extends WebServiceGatewaySupport {
//Works over here
#Retryable(maxAttempts=3, backoff=#Backoff(delay=100))
public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {
GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
return response;
}
/*
* Retryable is not working on the 2nd level methods in the bean.
* It works only with methods which are called directly from outside
* if there is 2nd level method, like this, Retryable is not working.
*/
//#Retryable
private Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
}
}
#Configuration
#EnableRetry
public class SphClientConfig {
#Bean
public SphIptvClient sphIptvClient() {
SphIptvClient client = new SphIptvClient();
return client;
}
}
So this is a super late answer, but since I've just come here and confronted the same problem (again, after years ago wrestling with transactions) I'll furnish a little more fleshed out solution and hopefully someone will find it useful. Suffice to say that #M. Deinum's diagnosis is correct.
In the above case, and to paraphrase Understanding AOP proxies, any place where SphIptvClient gets autowired will be given a reference to a proxy which Spring Retry will create when #EnableRetry is handled:
"The #EnableRetry annotation creates proxies for #Retryable beans" - Declarative Retry - Spring Retry
Once getSubscriberAccount has been invoked and execution has passed through the proxy and into the #Service instance of the object, no reference to the proxy is known. As a result sphRemoteCall is called as if there were no #Retryable at all.
You could work with the framework by shuffling code around in such a way as to allow getSubscriberAccount to call a proxy-ed sphRemoteCall, which requires a new interface and class implementation.
For example:
public interface SphWebService {
Object sphRemoteCall(String uri, Object requestPayload, String soapAction);
}
#Component
public class SphWebServiceImpl implements SphWebService {
#Retryable
public Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
}
}
#Service
public class SphIptvClient extends WebServiceGatewaySupport {
#Autowired
SphWebService sphWebService;
#Retryable(maxAttempts=3, backoff=#Backoff(delay=100))
public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {
GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) this.sphWebService.sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
return response;
}
}
#Configuration
#EnableRetry
public class SphClientConfig {
// the #Bean method was unnecessary and may cause confusion.
// #Service was already instantiating SphIptvClient behind the scenes.
}
#Retryable only works on the methods when called directly from other classes.
If you will try to invoke one method with #Retryable annotation from some other method of the same class, it will eventually not work.
// any call from this method to test method will not invoke the retry logic.
public void yetAnotherMethod() {
this.test();
}
// it will work
#Retryable(value = {RuntimeException.class}, backoff = #Backoff(delay = 1500))
public void test() {
System.out.println("Count: " + count++);
throw new RuntimeException("testing");
}
#Recover
public void recover() {
System.out.println("Exception occured.");
}
So, the output if test method is called, will be:
Count: 0
Count: 1
Count: 2
Exception occured.
But, if the yetAnotherMethod is called, output will be:
Count: 0
And a Runtime exception will be thrown.
Suppose you have a method which calls certain API - callAPI() and you want to implement retry logic over it, you can try use a do while, as it will execute only once, if successful.
Method to hit the external API
public int callAPI() {
return 1;
}
Method to implement retry logic
public int retrylogic() throws InterruptedException {
int retry = 0;
int status = -1;
boolean delay = false;
do {
// adding a delay, if you want some delay between successive retries
if (delay) {
Thread.sleep(2000);
}
// Call the actual method, and capture the response,
// and also catch any exception which occurs during the call.
// (Network down/ endpoint not avaliable
try {
status = callAPI();
}
catch (Exception e) {
System.out.println("Error occured");
status = -1;
}
finally {
switch (status) { //now based on error response or any exception you retry again
case HTTPStatus.OK:
System.out.println("OK");
return status;
default:
System.out.println("Unknown response code");
break;
}
retry++;
System.out.println("Failed retry " + retry + "/" + 3);
delay = true;
}
}while (retry < 3);
return status;
}
How can I execute try block statements in test case? My test case always goes to catch block.
I need to mock question bean, PostingResult, Question, dao all? I am confused. How I can test if/else ? Question class two fields are enums.
#Controller
#RequestMapping("/questionservice")
public class QuestionServiceController {
private static Log log = LogFactory.getLog(QuestionServiceController.class);
private final static Long SUCCESS = 000l;
private final static Long FAILURE = 999l;
#Autowired
QuestionAnswerDao questionAnswerDao;
#Autowired
QuestionAnswerDirectoryDao questionAnswerDirectoryDao;
#RequestMapping(value = "/postquestion", method = RequestMethod.POST)
public #ResponseBody PostingResult postQuestion(#RequestBody QuestionBean questionBean) {
System.out.println("In....");
PostingResult response = new PostingResult();
if (log.isDebugEnabled()) {
log.debug("QuestionBean: " + questionBean);
}
try {
System.out.println("in try");
Question question = questionAnswerDao.postQuestion(getQuestion(questionBean));
System.out.println("question"+question);
if (null != question) {
response.setStatus(SUCCESS);
response.setStatusMessage("Successfully saved..");
} else {
response.setStatusMessage("Question is null..");
response.setStatus(FAILURE);
}
} catch (Exception exp) {
if (log.isErrorEnabled()) {
log.error("Exception in processing " + questionBean + "; Exception: " + exp.toString());
}
response.setStatusMessage("Saving failed.. Exception: " + exp.toString());
response.setStatus(FAILURE);
}
return response;
}
It looks like you need to mock questionAnswerDao. Then tell Mockito to throw an exception when postQuestion() is invoked.
when(questionAnswerDao.postQuestion(/* specify args here */))
.thenThrow(new SomeException());
Then you can test the response object to see if it has the right messages and status.