Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I have a Spring JobRunner Component that has a run() method which throws my custom exception:
public Response run(final Request request, final String id) {
try {
execution = jobLauncher.run(job, parameters);
} catch (JobExecutionAlreadyRunningException e) {
throw new MyCustomException("already running ", e);
} catch (JobRestartException e) {
throw new MyCustomException("Restart Exception", e);
}
return generateResponse(request, id, execution);
}
In my service class, I call this run() method inside process()
protected Response process(final Request request, final String id) {
//get entity
//save
//bla bla
Response response;
try {
response = jobRunner.run(request, id);
updateStatus(entity, response.getStatus(), "");
} catch (MyCustomException ex) {
updateStatus(entity, FAILED);
throw new MyCustomException(ex.getMessage(), ex);
}
}
You will notice I am catching MyCustomException and rethrowing again. I have to catch because I need to update the status accordingly here in the service class as it has a repository dependency to update status in db for tracking purpose.
I am also rethrowing because attached to this custom exception is a ControllerAdvice for api requests so it responds accordingly.
What I would like to know, is this a good design or a bad practice? What could I change if anything?
This can be a good practice, bad practice, or unnecessary, depending on the case. When you catch and throw a new exception with:
throw new MyCustomException(ex.getMessage(), ex);
you get a new exception with a new stack trace that points to this line of code, and you maintain the original exception with its stack trace as the "cause".
This is a good practice if the original exception was produced in an asynchronous context. This is a common problem with reactive programming: when an exception is created by some asynchronous operation, its stack trace has no reference to the calling code. Creating a new exception at the point of capture adds the context needed to find the operation that resulted in the exception.
This is a bad practice if the original exception includes privileged information that should remain secret. In this case the new exception should be created without a cause, and the original exception should be logged. The code should look like:
log.error(ex);
throw new MyCustomException("whoopsie");
In other cases, creating a new exception instance is unnecessary. Just re-throw the original exception.
} catch (MyCustomException ex) {
updateStatus(entity, FAILED);
throw ex;
}
This is a very good question actually.
I see that you are separating concerns by layers. You should do the same with your exceptions.
And you should also be aware of the type of exceptions you are throwing (checked vs. unchecked).
JobCustomException
I'd advise you to create a Checked Exception for your Jobs. Here you would encapsulate all the details related to a job error. And by using a checked exception, you are achieving two things:
You are warning the calling class that an exception may be thrown and can be handled.
You are signalling the developer that this may be a recoverable error.
From the Java Docs:
If a client can reasonably be expected to recover from an exception,
make it a checked exception. If a client cannot do anything to recover
from the exception, make it an unchecked exception
ServiceCustomException
Then, another exception can be created for your service layer. This kind of exception should extend from RuntimeException if you believe it may no longer be handled (except for the ControllerAdvice, of course).
It may also be useful to transform or introduce extra information.
Finally, while you may have a single generic Job exception, there may be many client classes (services) that may throw different Runtime-based exceptions to add further business detail about the business flow where the error occurred.
Throwing, catching, and re-throwing
You are doing it right, as long as you pay attention to the aforementioned bullets. This is the whole point of throwing exceptions.
Exceptions don't need to be terminal errors, although there is some kind of belief that RuntimeExceptions are the only accepted type of Exceptions.
Most well-designed object's APIs and frameworks, make use of proper Checked exceptions to communicate to the client classes what are the errors that may occur.
Related
So im reading the below and i understand why you would do it..
https://jenkov.com/tutorials/java-exception-handling/exception-wrapping.html
example :
try{
dao.readPerson();
} catch (SQLException sqlException) {
throw new MyException("error text", sqlException);
}
So what if i want to isolate all external exceptions inside the dao layer only, and only use my exceptions. so in the above example i dont want to send SQLEXception inside the constructor, would doing the below be enough. Would it contain enough information :
throw new MyException("error text", sqlException);
or maybe my constructor should be the following instead
public MyException(String text,Exception ex)
You can inherit your exception from Exception like
MyException extends Exception {
}
and then use try catch like :
try {
dao.readPerson();
} catch (MyException ex) {
// handle Exception
}
by doing this you can do whatever you want in your class and i think its cleaner than other ways.
It will trigger automatically so you dont need to raise an exception.
If you want to catch SQL Exceptions only you can inherit MyException from SqlException so it will only trigger when SqlException happens
I understand that you worry about the catching part of your code knowing about the wrapped exception. In "not knowing" there are different layers.
Level 1: Nothing obligates the catching class to get the cause and therefor explicitly knowing about the SQLException. They just catch the MyException and don't care what's wrapped in it. Usually that's enough.
Level 2. Another level of not knowing is restricting the catching class to even have access to the wrapped exception. In that case why wrap it at all? Just catch the SQLException in your DAO layer and throw MyException without wrapping the original exception in it.
About wrapping the causing Exception instead of the original one. You could do that but you might lose valuable information so 99% of the time it's not recommended. There are some corner cases where I've done it though. Let's say you throwing code runs asynchronously through ExecutorService. Then if an exception is thrown it's wrapped to ExecutionException, but as a caller I might not be interested that the code ran asynchronously so I catch the ExecutionException, get it's cause (the actual exception that happened) and wrap that to my custom exception.
My understanding of these exceptions is if an object in the database that you are looking for doesn't exist or exists these gets thrown? But is it ok for myself to use when I want to handle different cases in MyServiceClass.
Is it bad practice to throw these exceptions or should I create my own Exceptions for let's say if a user dont exist in the database?
How does it work in a real production?
Thanks in advance!
You should only implement a custom exception if it provides a benefit compared to Java's standard exceptions. The class name of your exception should end with Exception.
But it’s sometimes better to catch a standard exception and to wrap it into a custom one. A typical example for such an exception is an application or framework specific business exception. That allows you to add additional information and you can also implement a special handling for your exception class.
When you do that, make sure to set the original exception as the cause. The Exception class provides specific constructor methods that accept a Throwable as a parameter. Otherwise, you lose the stack trace and message of the original exception which will make it difficult to analyze the exceptional event that caused your exception.
public void wrapException(String input) throws MyBusinessException {
try {
// do something
} catch (NumberFormatException e) {
throw new MyBusinessException("A message that describes the error.", e);
}
}
Try not to create new custom exceptions if they do not have useful information for client code.
And if you make a custom exception be sure to:
Document the Exceptions You Specify
Throw Exceptions With Descriptive Messages
Catch the Most Specific Exception First
Don’t Log and Throw
Currently my signup looks like this:
public void signup(User newUser) throws Exception {
log.info("Sign up: " + newUser.getEmail());
if (restService.emailAlreadyExists(newUser.getEmail())) {
throw new Exception("Email already in use.");
}
List<Role> roles = new ArrayList<Role>();
roles = roleRepository.findAllOrderedByName();
roles.add(roleRepository.findByName("user"));
newUser.setRoles(roles);
newUser.setPassword(restService.getHashedValue(newUser.getPassword()));
try {
em.persist(newUser);
} catch (Exception e) {
throw new Exception("Just noobs use apps with less bugs. Try again. Now!");
}
log.info(newUser.toString());
userEvent.fire(newUser);
}
In first order I'm just interested in two messages (will become FacesMessage) for the user. To prevent other cryptic messages for the user, I even would need to extend the try-block up to roles.
Well, that would be bad practice, I guess. Also using a generic Exception smells, they say. But: I detect following documented Exceptions in this small piece of code:
IllegalStateException
IllegalArgumentException
EntityExistsException
TransactionRequiredException
ObserverException
Even not speaking about the eight(!) Exceptions of the method getSingleResult() of javax.persistence.TypedQuery.
Should I really handle all Exceptions in this example, or is it ok to skip a few (and/or maybe even use a generic Exception like above).
Best practice is: catch all exceptions separately and make as more custom log messages as possible. It's easier to understand faulty situations and to act accordingly.
Reality in a typical business application is: try to group your exceptions (ex. group all possible exceptions caused by your method input, all the ones throwed by the db etc. etc.) and learn that sometimes you will put the infamous catch (Exception e) or rethrow a generic exception with a generic message... or people will start calling you about logs growing like elephants.
I have a Java 1.4 web app that uses Hessian to make web service calls. I'm trying to write it as robust and transparent as possible. I don't want any hessian exceptions to make it out of my delegate class when calling the proxy. I want to be able to catch any Hessian runtime exceptions, unwrap them and either rethrow some of the major exceptions (e.g., ConnectException) or rewrap them in another exception. But finding these exceptions are a bit of a challenge.
Every method I have in my delegating class has this sort of structure.
public MyResult myMethod(MyArgsType myArgs)
throws ConnectException
{
try
{
return proxy.myMethod(myArgs);
}
catch (HessianRuntimeException ex)
{
Throwable cause = ex.getCause();
if (cause instanceof ConnectException)
throw (ConnectException)cause;
throw new MyRuntimeException(cause);
}
}
This has been working fine in my own tests but some other runtime exceptions are making it out and I'm missing them. For instance, we recently found that it may throw a HessianConnectionException caused by a SocketException (the remote server was down). I was fully expecting that what I currently have would have caught that. The HessianConnectionException doesn't derive from the HessianRuntimeException so now I have to add that exception to be caught.
public MyResult myMethod(MyArgsType myArgs)
throws SocketException, ConnectException
{
try
{
return proxy.myMethod(myArgs);
}
catch (HessianException ex) // HessianConnectionException derives from HessianException
{
Throwable cause = ex.getCause();
if (cause instanceof SocketException)
throw (SocketException)cause;
throw new MyRuntimeException(cause);
}
catch (HessianRuntimeException ex)
{
Throwable cause = ex.getCause();
if (cause instanceof ConnectException)
throw (ConnectException)cause;
throw new MyRuntimeException(cause);
}
}
Ok that's fine with me, it had to be done... but where does it end? I can't find any documentation on what other runtime exceptions that I need to be aware of or any of their underlying causes.
I'm hoping just catching HessianException and HessianRuntimeException would be enough since those are the only runtime exceptions that I can find listed here. But with these newly found issues, I'm not sure what exceptions I would want to unwrap since it seems like it's a mixed bag (as far as I knew, SocketException and ConnectException were different representations of the same problem).
Is there any documentation in what runtime exceptions are thrown when calling methods through the proxy and all their underlying causes?
I assume you have done web searches etc., so here is a fallback position in case you don't find the documentation you are looking for.
After your existing catches, catch RuntimeException. Test whether the Exception's class name begins with "Hessian" and/or check its package name. If it is not Hessian-originated, rethrow.
After you know it is a Hessian exception you can do something similar to your current strategy, looking through the cause chain for something that makes sense in your context or at least is from a non-Hessian package.
Have this method call:
->
simpleJdbcTemplate.queryForInt(SQL,null);
->
queryForInt() method in the springs SimpleJdbcTemplate throws a DataAccessException which is a runtime exception. I want to propegate exceptions to the view tier of the application since Spring frame work Wraps Checked Exceptions inside RuntimeExceptions I stuck here.
How do I do this?
Explanation 1:
The value-add provided by the Spring Framework's JDBC abstraction framework- they say The Spring Framework takes care of all except 3 and 6. 3 and 6 need to be coded by an application developer
Define connection parameters
Open the connection
Specify the statement
Prepare and execute the statement
Set up the loop to iterate through the results (if any)
Do the work for each iteration
Process any exception
Handle transactions
Close the connection
But if I encounter a situation where the connection to the database losses after certain time the program started. Then a runtime exception will be thrown when a call to the above method made.since I don't handle the exception I cannot inform the user interface (view).
Just because Spring throws a runtime exception doesn't mean you cannot catch it. If you want to do something special for DataAccessExceptions, you can certainly do that:
try {
// query logic
} catch (DataAccessException ex) {
// handle the exception
}
If you're using Spring's MVC framework, it may be worth looking into the ExceptionResolver interface. It's a mechanism for deciding how to handle all those uncaught exceptions thrown by the lower layers of the application. It gives you one last chance to display a different view based on exceptions that are thrown.
It depends if your view tier catches checked exceptions (any subclass of throwable that does not subclass RuntimeException or Error, or are not instances of RuntimeException or Error directly) or unchecked exceptions (RuntimeException or Errors or subclasses of these Throwable subclasses).
Generally, you'll either have something like this:
try {
//... processing
} catch(Exception/RuntimeException e) {
// propagate the exception to the view in a meaningful manner
}
If this is the case, for a runtime exception, you don't have to do anything - the block will catch the runtime exception.
If you want to convert it to checked, assuming you're using a version of Java that supports wrapped exceptions, all you have to do is:
try {
//...spring code
} catch(DataAccessException e) {
throw new Exception(e);
}
Then, your layer above this processing will catch it as a checked exception.
Do you just want to be able to access the original exception information in your View? If so, you should be able to invoke getCause() on the RuntimeException to get the underlying checked Exception that caused it. Otherwise you would need to add a "throws" declaration to your methods that are utilizing SimpleJdbcTemplate, catch DataAccessException and rethrow the checked Exceptions that are wrapped.