assylias explain well about final rethrow.
I added final to method3.
public void method4() throws IOException {
try {
throw new IOException("1");
} catch (final Exception e) {
e = new IOException("2"); //does not compile
throw e; //does not compile
}
}
I set my compiler to 1.7. method4 have two compile errors :
final exception can neither be reassigned nor throw precise exception.
So, explicit final exception is only used to prevent modify?
Exception of catch block is implicitly final that doesn't mean you can not reassign it. If you specifically make it final then compiler will not allow you to modify that reference. To make throw compile exception instance must be final or effectively final as already covered in linked answer.
public void method4() throws IOException {
try {
throw new IOException("1");
} catch (final Exception e) {
e = new IOException("2");// You can not modify final reference
throw e;
}
}
so explicit final exception is only used to prevent modify?
Yes exactly, in case of exception, final modifier is redundant. It is always recommended to throw or log the exception. Modification of any exception is an anti pattern according to me. Generally speaking even in case of custom exception we should not modify the thrown exception unless and until you have very strong reason to do so.
Related
I have a very basic create user controller.
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createUser(#RequestBody UserInput userInput) {
userControllerService.createUser(userInput);
return ResponseEntity.ok("success");
}
The UserControllerService#createUser method is a transaction containing multiple SimpleJpaRepository#save calls. E.g.
#Transactional
#Override
public void createUser(UserInput userInput) {
userRepository.save(userInput);
profileRepository.save(userInput.getProfile());
}
I would like to be able to have the db handle unique constraint violations and be able to inform the client about a specific violation.
For example if I want to inform the client if and only if I get specific constraint violations.
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createUser(#RequestBody UserInput userInput) {
try {
userControllerService.createUser(userInput);
} catch (DuplicateUserNameException e) {
return new ResponseEntity<>("", HttpStatus.BAD_REQUEST);
} catch (DuplicateEmailException e) {
return new ResponseEntity<>("", HttpStatus.BAD_REQUEST);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return ResponseEntity.ok("success");
}
However any constraint violation throws a DataIntegrityViolationException at the end of UserControllerService#createUser. And DataIntegrityViolationException is too brittle to rely on. It only has the cause SQLState: 23505 (unique constraint violation) and an unstructured message, such as:
ERROR: duplicate key value violates unique constraint "unique_user"
Detail: Key (email)=(john#test.com) already exists.
Even if I add custom exception handling, it will never be run since the DataIntegrityViolationException isn't encounter until the end of the method when the db is actually called for the first time. E.g. this has no effect.
#Transactional
#Override
public void createUser(UserInput userInput) {
try
userRepository.save(userInput);
} catch (Exception e) {
throw new DuplicateUserNameException();
}
try {
profileRepository.save(userInput.getProfile());
} catch (Exception e) {
throw new DuplicateEmailException();
}
}
Am I going about this the wrong way? It seems like this is very basic functionality that should be possible somehow.
The best way I can think of is adding some code to parse the message from DataIntegrityViolationException, but this has its limitations, for example, for two inserts into the same table have a different meaning for the application. One insert might be directly from the user and the second might be something the application generates. It may not be possible to distinguish the two from the end of the transaction by just parsing the detailed message.
Are there other implementations I should consider instead?
If I understand correctly , it sounds like you want to have a reliable way to determine when DataIntegrityViolationException is thrown , what is the exact reason that causes it such as whether it is due to the duplicated email or duplicated username for a particular use case or anything else.
The simplest way is not to rely on the thrown exception to determine but actively issue some SQL to validate it before the data is saved to DB such as :
#Transactional
#Override
public void createUser(UserInput userInput) {
if(userRepository.existUsername(userInput.getUsername()){
throw new DuplicateUserNameException();
}
if(userRepository.existEmail(userInput.getEmail())){
throw new DuplicateEmailException();
}
userRepository.save(userInput);
profileRepository.save(userInput.getProfile());
}
The problem you mentioned
Even if I add custom exception handling, it will never be run since the DataIntegrityViolationException isn't encounter until the end of the method when the db is actually called for the first time. E.g. this has no effect.
should be solvable by telling Hibernate to execute the transaction right away, by calling
userRepository.flush();
This should cause the exception to be thrown on that line, at least.
I have the following below piece of code when I am running SonarQube for code quality check on it after integrating it with Maven.
However, Sonar is complaining that I should Either log or rethrow this exception.
What am I missing here? Can some one help me please.
Code
public ShippingResponse processShipping(ShippingRequest request) {
log.debug("Processing Reservation Request ....");
try{
return helper.processShippingMethod(request);
} catch (ServiceException serviceException) {
log.error(RESERVATION_EXCE, ExceptionUtils.getStackTrace(serviceException));
throw serviceException;
} catch (Exception e) {
throw new ServiceException(ErrorMessages.EPO_SM_ERR_03, e.getMessage());
}
}
The point that Sonar is trying to make is that you ideally print or keep the root cause of your exception, so basically the stack. You keep it by passing the exception object because if you only keep the message you lose all that information. To make sonar happy you either print the stack trace (log.error(ErrorMessages.EPO_SM_ERR_03, e)), or re-throw a new exception passing the Throwable object to the constructor.
So the ideal solution would be to use the ServiceException like this;
public class ServiceException extends Exception {
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
}
throw new ServiceException(ErrorMessages.EPO_SM_ERR_03, e);
I have the following method:
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void applyLog(int codFilial, List<LogLojaCentralCompactoEntity> items) {
}
which internally calls:
#Override
#Transactional(noRollbackFor = PersistenceException.class)
public void apply(LogCompactoEntity entity) {
}
The second method has a try/catch a PersistenceException. The problem is the transaction rolls back then it reaches PersistenceException.
I know Spring #Transactional defaults to roll back in any unchecked exception, but I am explicitly telling noRollbackFor the PersistenceException.
Why its not working? Any way to threat it?
Thanks.
Edit - the try/catch method inside apply does this:
try {
insert();
}
catch(PersistenceException e)
{
update();
}
Edit2 - log:
Edit3 - exception handling part:
if (acao.equalsIgnoreCase("I")) {
try {
insertQuery.executeUpdate();
}
catch(PersistenceException e) {
int affected = updateQuery.executeUpdate();
if(affected == 0)
throw new LogApplyException("O insert falhou e o update não afetou registros.");
}
}
Edit4 - Some more info:
1) Removing #Transactional from apply, leaving #Transaction just on applyLog results on this exception:
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute statement
2) Same as 1, but adding noRollbackFor = PersistenceException.class (on applyLog) results on this exception:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
Edit 5:
#lzagkaretos solution is valid (Thank you), but I want to know how to handle this for future cases.
UPDATE
I think that relying in primary key violation exception in order to find if record should be inserted or updated is not something you should do. Another implementation you can use instead is finding before the execution if record is already saved in the database to perform an update, or not to perform an insert.
For example, if you can use spring data repositories, the code might seem like this.
public void apply(LogCompactoEntity entity) {
LogCompactoEntity logCompactoEntity = (entity.getId() == null) ? new LogCompactoEntity() : logCompactoRepository.findOne(entity.getId());
if (logCompactoEntity == null) {
logCompactoEntity = new LogCompactoEntity();
}
copyProperties(entity, logCompactoEntity);
logCompactoRepository.save(logCompactoEntity);
}
--
Maybe you shouldn't have a try/catch block in apply method and declare it with throws PersistenceException in method signature.
In order for noRollbackFor to work, PersistenceException has to be thrown from apply method. You can try the following.
try {
insert();
}
catch(PersistenceException e) {
update();
throw e;
}
I am trying to implement a generic class that executes a callable asynchronously, but I am not sure about the semantics.
#Component
public class MyCallerImpl implements MyCaller {
#Async
#Override
public <T> Future<T> runAsync(Callable<T> callable) throws Exception {
return new AsyncResult<T>(callable.call());
}
}
Basically, this component executes arbitrary actions from any callable asynchronously using the #Async annotation.
I am unsure about the Exception in the throws clause of the method signature.
A Junit test:
#ContextConfiguration("classpath:test-config.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class RunnerTest{
#Resource(name="myCallerImpl")
private MyCaller myCaller;
#Test
public void testException(){
final Callable<String> callable = new Callable<String>(){
#Override
public String call() throws Exception{
throw new MyException("foobar");
}
};
try
{
final Future<String> future = myCaller.runAsync(callable); // this can throw Exception due to Callable.call()
future.get(); // this can throw InterruptedException and ExecutionException
}
catch (final InterruptedException ie)
{
// do someting
}
catch (final ExecutionException ee)
{
// we want to check the cause
final Throwable cause = ee.getCause();
assertTrue(cause instanceof MyException);
}
catch (final Exception e)
{
// Not sure what to do here.
// Must be caught as it is declared to
// be thrown from the MyCaller.runAsync() method
// but nothing will really ever get here
// since the method is #Async and any exception will be
// wrapped by an ExecutionException and thrown during Future.get()
fail("this is unexpected);
}
My question is what to do about the Exception declared in the throws clause of MyCallerImpl.runAsync()?
The only reason I have declared it is because of the way I am calling the callable. Originally I had the following in the async method:
FutureTask<T> futureTask = new FutureTask<T>(callable);
futureTask.run();
return futureTask;
But when an exception is thrown from the callable in that instance, it gets wrapped twice in an ExecutionException, the first time when FutureTask.run() is called eventually FutureTask.Sync.innerRun() catches the exception and calls innnerSetException() and a second time when the AsyncExecutionIntercepter gets the result from the Future via Future.get(), which eventually again checks if there is an exception and throws a new ExecutionException wrapping the ExecutionException caught in innerRun()
I also tried to do the following in the method:
FutureTask<T> futureTask = new FutureTask<T>(callable);
return futureTask;
I had figured that since the AsyncExecutionInterceptor calls Future.get(), that the callable would be called immediately, but that was not the case. It just hangs on FutureTask.acquireSharedInterruptibly() and never returns.
Maybe I'm in over my head here. It works how I have it set-up with the callable now, but I rather not have the method signature declare a throws Exception.
Any advice? Should I forget about this generic way of doing async calls with a callable?
There are 2 layers of exception here.
one:
the exception leading to the calling of the Callable
if (string.equals("X")){ callable.call();}
two:
the exception caused when calling the callable.call() method (your "throw new MyException("foobar");")
since you do not have any other code prior to "callable.call();", it would be safe to remove the checked exception.
Change
public <T> Future<T> runAsync(Callable<T> callable) throws Exception
to
public <T> Future<T> runAsync(Callable<T> callable)
additionally, you can code it this way
final Future<String> future = myCaller.runAsync(callable);
try
{
future.get(); // this can throw InterruptedException and ExecutionException
}
catch (final InterruptedException ie)
{
// do someting
}
catch (final ExecutionException ee)
{
// we want to check the cause
final Throwable cause = ee.getCause();
assertTrue(cause instanceof MyException);
}
Is there any way to throws an standard or custom exception with Apex method as,
private void createNewJob() throws RecordNotFoundException {
try {
// Some DML operation
} catch (Exception e) {
System.Debug('Error: Object not found');
throw new RecordNotFoundException('Object not found');
}
}
Yes.
Define the exception class. Minimally, :
public class RecordNotFoundException extends Exception { }
Your throw statement looks fine.
Also, there's no need to declare that the method throws an exception type.
I think following can help you. It has complete example of SFDC APEX standard and custom exceptions
http://share-salesforce.blogspot.in/2013/05/salesforce-apex-exception-handling-and_29.html
We should not explicitly throw those standard or custom exceptions with Apex, code it self done that.
#Method that throws some custom exception (RecordNotFoundException)
private void createNewJob(){
try {
// Some DML operation
} catch (Exception e) {
System.Debug('Error: Object not found');
throw new RecordNotFoundException('Object not found');
}
}
#Can handle that exception as follow,
private void callingMethod() {
try {
createNewJob(); // Call above method
} catch (RecordNotFoundException e) {
System.Debug('Error:Record not found exception ['+ e.getMessage()+']');
}
}