Quick question regarding Java Micrometer with #Counted and #ExceptionHandler please.
I have a very straightforward #ExceptionHandler:
#ExceptionHandler
#Counted(value = "MY_EXCEPTION", description = "SOME_DESCRIPTION")
public Mono<ResponseEntity<String>> myCoolExceptionHandler(final RuntimeException runtimeException) {
System.err.println("an exception!" + runtimeException);
return Mono.just("bad");
}
I think this combination is quite interesting, as it gives visibility on exception happening. We can build dashboard, alerts, etc, quite cool.
Unfortunately, when I looked at the metric generated, it was something like:
# HELP MY_EXCEPTION_total SOME_DESCRIPTION
# TYPE MY_EXCEPTION_total counter
MY_EXCEPTION_total{class="package.MyController",exception="none",method="myCoolExceptionHandler",result="success",} 3.0
I am quite puzzled on exception="none" and result="success"
May I ask how those values got into the metric in the first place?
Also, how to change them into something more meaningful, such as the exception class for instance?
Thank you!
The annotated method itself does not throw an exception and always completes normally. Therefore, the intercepting code installed by the annotation will never record an exception (exception=none) and the outcome will always be good (result=success). Only exceptions thrown from within the annotated method will be recorded as an error outcome.
You can always manually record metrics by injecting the MetricRegistry and then registering a metric with the appropriate name and tags.
Related
I need to write a RestClient used by several application that return a specific object.
In case of bad request (400) I'd like to advice the caller application of the message error and status code.
I wonder if is it a good behavior to throw a managed Exception with code and message property in order to be catched properly from the caller code.
Something like this:
RestClient.java
ClientResponse response;
try {
response = client.resource(requestURI).queryParams(queryParams)
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.post(ClientResponse.class, richiesta);
boolean output = response.getStatus() == Response.Status.NO_CONTENT.getStatusCode();
if (!output && response.getStatus() == Response.Status.BAD_REQUEST)
throw new RestClientRuntimeException(response.getStatus, response.getEntity(String.class));
} catch (ClientHandlerException e) {
throw new RestClientRuntimeException(e);
} catch (UniformInterfaceException e) {
throw new RestClientRuntimeException(e);
}
Caller application
try
{
boolean output = restClient.method()
} catch (RestClientRuntimeException e) {
// read exception status and message entity
}
Is it a good practice ?
Your question has several aspects. Lets consider them separately:
Should I translate http error codes into exceptions?
Yes you should translate error codes into exceptions. Exceptions are nice (if used correctly of course). They separate the happy path from the exceptional cases. If you don't use exceptions but return codes, your callers need to check for that return code. And if you call several methods you get some nasty cascading ifs:
if (service1.method1() == NO_ERROR) {
if (service2.method2() = NO_ERROR) {
if (service3.method2() = NO_ERROR) {
...
} else {
...
}
} else {
...
}
} else {
...
}
Furthermore if you have several layers (and you almost certainly have), you need to do this at every level again. Exceptions are much better here. They let you code (and read!) the nice and clean happy path first and then
you can worry about the exceptions in the catch block. That's easier to write and easier to read.
Should I use checked exceptions?
This can get quite religious. Some people think checked exceptions are a good idea, some think, they are evil. I don't want to debate that here in detail. Just that: Do you want to force your callers to think about that particular exception? Is there always or at least in the vast majority of cases a way for the caller to handle that exception?
If you come to the conclusion that the caller cannot do anything but log the exception and fail itself, you should refrain from using checked exceptions. This makes the caller code much cleaner. There is no use in pointless try..catch blocks that are just there to make the compiler happy.
Speaking of REST: In case of a 500, there is most likely no chance to recover. If you get a 405 you most likely have a bug and there is no way to recover either. If you get a 409 Conflict with some special info on how to resolve the conflict there might be a good way to handle that (depending on your requirements). In such a case you may consider a checked exception.
Should I store the response code in the exception?
When you use generic RestClientRuntimeExceptions and have your callers query the response code, then your caller is obviously coupled to this being a REST call. You can do that if you are writing a generic REST client that can query arbitrary REST APIs. No problem in this case.
But you are already using a generic library (I guess Jersey). So what's the point in wrapping a generic API around a generic API? There may be some reasons (e.g. evaluating some internally used header params) but you should think about it whether this is justified.
Maybe you want to write an application-specific REST client, i.e. one that deserializes your application-specific representation classes (DTOs). In such a case you should not use generic exceptions but rather application-specific ones. If you use application-specific exceptions your callers are not coupled to this being a REST call. Maybe in the future a cool new technology shows up and you need to change your client. If you use the generic exceptions and have your callers evaluate the response codes, the callers need to change, too. But if you use application-specific exceptions, this interface will be more stable. The callers don't even need to know that such a thing as REST exists. This makes the caller code simpler and more maintainable as you separate concerns.
Should I put a rest client into a jar?
I know you haven't asked that question. But maybe you should do so. Providing a rest-client jar with a Jersey dependency to arbitrary clients (that's what it seems to me that you do) looks nice at first but can get you into real trouble with dependencies. If you are not convinced, I can explain that in detail but lets discuss this in a separate question.
Since your success response is JSON, I would model the error responses as JSON too.
For example consider the following JSON serialized from a POJO like ErrorMessage.java having corresponding fields.
{
"statusCode": 400,
"errorMessage": "Bad Request",
"errorDetails": "<details here>"
}
Since it is HTTP, it would be better to communicate error codes based on HTTP status codes. errorMessage and errorDetails can be blank in case of a successful status code.
Why don't you check the HTTP status code family?
Other errors besides 400 can happen. If you test the status code family, you catch them all:
switch (response.getStatusInfo().getFamily()) {
case CLIENT_ERROR:
// It's a client error
// You can throw an exception
break;
case SERVER_ERROR:
// It's a server error
// You can throw an exception
break;
default:
break;
}
IMHO it's a good practice at least for 400 status code to have custom exception because it implies malformed data was sent.
Instead of throwing unchecked exception and catching them, checked exceptions are more suitable.
Using Spring's JdbcTemplate, I've been trying to figure out a clean way to log exceptions in the DAO layer, but can't seem to figure it out. I want to log the SQL statement that was used and the parameters.
For example, where addStoreSql is a parameterized statement
public int addStore(Store store) {
return jdbcTemplate.update(addStoreSql, store.getId(), store.getName());
}
I'm doing something like..
public int addStore(Store store) {
try{
return jdbcTemplate.update(addStoreSql, store.getId(), store.getName());
} catch (DataAccessException ex) {
logger.error("exception on deleting store - " + store.toString(), ex);
throw ex;
}
}
My question, is there a way to write this any cleaner across many dao methods? Possibly at the logger level or some Spring library? Or is this the cleanest way (Or is the above code even bad)?
I have multiple methods that do basically the same thing, take in a object, pass the fields to a query and return the result.
The difficulty of doing this with Spring is that the JDBC objects that you would want to get this information from are not Spring-managed objects, they're created by the driver. So Spring AOP won't apply (without using AspectJ).
Spring can supply the query and parameters separately for you, if you log the category "org.springframework.jdbc.core.JdbcTemplate" at DEBUG level and "org.springframework.jdbc.core.StatementCreatorUtils" at TRACE level.
There are existing libraries log4jdbc and p6spy that implement a wrapper around the JDBC driver, in order to generate a SQL statement with the parameters inserted in place. See this question. Using either of these should be a matter of adding the jar to the project, changing your jdbc url to point to the wrapper, and tweaking the logging to get the level of information you want.
The existing logging code is not good because it is repetitious cut-n-paste code, and it will result in exceptions being logged multiple times. The logs will be harder to read and will roll more frequently.
Definitely don't use this pattern:
logger.error("exception on deleting store - " + store.toString(), ex);
throw ex;
because if often leads to duplicates log entries. There should be some global trap for exceptions and its responsibility is to log the error.
EDIT
By global trap for exceptions, I mean that every application should have some mechanism for catching most (ideally all) exceptions from Java code and log them. Imagine that you don't catch and miss log for some important error. You are than blind when trying to figure out what happened in production.
So let's pretend that we have such exception logging mechanism in place. Your pattern would log SQL error and throw exception that would be catched by global exception trap and logged again. You don't want that to happen, so don't log it in your code, save one line of code and don't create duplicate log entry.
I'm pondering on exception handling and unit tests best practices because we're trying to get some code best practices in place.
A previous article regarding best practices, found on our company wiki, stated "Do not use try/catch, but use Junit4 #Test(expect=MyException.class)", without further information. I'm not convinced.
Many of our custom exception have an Enum in order to identify the failure cause.
As a result, I would rather see a test like :
#Test
public void testDoSomethingFailsBecauseZzz() {
try{
doSomething();
} catch(OurCustomException e){
assertEquals("Omg it failed, but not like we planned", FailureEnum.ZZZ, e.getFailure());
}
}
than :
#Test(expected = OurCustomException.class)
public void testDoSomethingFailsBecauseZzz() {
doSomething();
}
when doSomethig() looks like :
public void doSomething throws OurCustomException {
if(Aaa) {
throw OurCustomException(FailureEnum.AAA);
}
if(Zzz) {
throw OurCustomException(FailureEnum.ZZZ);
}
// ...
}
On a side note, I am more than convinced that on some cases #Test(expected=blabla.class) IS the best choice (for example when the exception is precise and there can be no doubt about what's causing it).
Am I missing something here or should I push the use of try/catch when necessary ?
It sounds like your enum is being used as an alternative to an exception hierarchy? Perhaps if you had an exception hierarchy the #Test(expected=XYZ.class) would become more useful?
If you simply want to check that an exception of a certain type was thrown, use the annotation's expected property.
If you want to check properties of the thrown exception (e.g. the message, or a custom member value), catch it in the test and make assertions.
In your case, it seems like you want the latter (to assert that the exception has a certain FailureEnum value); there's nothing wrong with using the try/catch.
The generalization that you should "not use try/catch" (interpreted as "never") is bunk.
Jeff is right though; the organization of your exception hierarchy is suspect. However, you seem to recognize this. :)
If you want to check the raw exception type, then the expected method is appropriate. Otherwise, if you need to test something about the exception (and regardless of the enum weirdness testing the message content is common) you can do the try catch, but that is a bit old-school. The new JUnit way to do it is with a MethodRule. The one that comes in the API (ExpectedException) is about testing the message specifically, but you can easily look at the code and adapt that implementation to check for failure enums.
In your special case, you want to test (1) if the expected exception type is thrown and (2) if the error number is correct, because the method can thrown the same exception with different types.
This requires an inspection of the exception object. But, you can stick to the recommendation and verify that the right exception has been thrown:
#Test(expected = OurCustomException.class)
public void testDoSomethingFailsBecauseZzz() {
try {
doSomething();
} catch (OurCustomException e) {
if (e.getFailureEnum.equals(FailureEnum.ZZZ)) // use *your* method here
throw e;
fail("Catched OurCostomException with unexpected failure number: "
+ e.getFailureEnum().getValue()); // again: your enum method here
}
}
This pattern will eat the unexpected exception and make the test fail.
Edit
Changed it because I missed the obvious: we can make a test case fail and capture a message. So now: the test passes, if the expected exception with the expected error code is thrown. If the test fails because we got an unexpected error, then we can read the error code.
I came across this when searching how to handle exceptions.
As #Yishai mentioned, the preferred way to expect exceptions is using JUnit rules and ExpectedException.
When using #Test(expected=SomeException.class) a test method will pass if the exception is thrown anywhere in the method.
When you use ExpectedException:
#Test
public void testException()
{
// If SomeException is thrown here, the test will fail.
expectedException.expect(SomeException.class);
// If SomeException is thrown here, the test will pass.
}
You can also test:
an expected message: ExpectedException.expectMessage();
an expected cause: expectedException.expectCause().
As a side note: I don't think using enums for exception messages/causes is good practice. (Please correct me if I'm wrong.)
I made catch-exception because I was facing the same problem as you did, Stph.
With catch-exception your code could look like this:
#Test
public void testDoSomethingFailsBecauseZzz() {
verifyException(myObj, OurCustomException.class).doSomething();
assertEquals("Omg it failed, but not like we planned", FailureEnum.ZZZ,
((OurCustomException)caughtException()).getFailure() ;
}
Let's say I have an interceptor that looks smth like this:
public class AuthorizationInterceptor {
Logger log = Logger.getLogger(getClass().getName());
#AroundInvoke
private Object authorize(InvocationContext ic) throws Exception{
// ... some other logic for authorization
if (!allowedMethods.contains(ic.getMethod().getName())){
log.info("Authorization failed. Preparing to throw exception");
throw new AuthException("Authorization failed for method " +
ic.getMethod().getName());
}
return ic.proceed();
}
}
which is applied to different methods from my EJBs.
I would normally expect the exception throed to be passed to the invoking client, like all normal EJB exceptions.
Apparently this doesn't happen if I throw it from an Interceptor... It's not even logged on the server; like it's never thrown although it is - the return statement is never executed.
What am I doing wrong?
I'm using GF 3.0.1
After searching a bit for this issue, I found this SO post which was answered a few minutes ago. Quote:
I don't think there is a correct way
to do that. Methods should throw only
the exceptions they declared, and an
interceptor shouldn't add a new one.
My personal case got fixed by adding
an error code to our default exception
which is thrown by all methods.
Question author is the same person who answered and accepted this answer, so I guess he was trying to solve the same issue as you and came to conclusion that it cannot be done.
Here are a couple of things to try:
1. Check that the authorize(...) method is called.
2. Try making the authorize(...) method public instead of private.
3. Check that the EJB has an annotation like this:
#Interceptors(AuthorizationInterceptor.class)
I've got a unit test for a Seam component that should fail if a value isn't provided in the context before the backing bean is created. I've been able to test this manually but would like to cover this scenario with a unit test.
I'm basically getting a org.jboss.seam.InstantiationException caused by a java.lang.IllegalArgumentException when Seam tries to create the backing bean. This is good and is what I'd expect to happen. The problem is that when I write the unit test, I can neither put a try/catch around the new FacesRequest(..) {}.run(); or use the expectedExceptions annotation. In both cases, the exception is not caught and causes the test to fail. I assume this is because of Seam's exception filter but I don't know enough about the filter to know what the correct pattern to test this..
My code with the annotation looks something like this:
// also tried IlligalArgumentException here
#Test( enabled = true, expectedExceptions = InstantiationException.class )
public void noDataTest() throws Exception
{
login( USERNAME );
// the stack trace says that the test fails on the next line.
// this is expected.
new FacesRequest( "/blah/blah/show.xhtml" ) {
#Override
protected void updateModelValues() {
}
#Override
protected void invokeApplication()
{
// we should never get here
// i'll put an failure here eventually
}
}.run();
}
I found the answer. Hopefully this helps someone else who was banging their head against the wall..
I was looking for a specific Exception but Seam was catching that Exception, asserting that an error had occurred, and then throwing a java.lang.AssertionError (java.lang.Error, not java.lang.Exception). Catching the correct Throwable and using the correct type in the annotation now work..
looks to me that your test case is expecting a empty no-arg constructor in backing bean whic is probably missing