I work on a Spring webStart application...
I have 2 (and potentially more) methods that process multi-tier Exception clause, as in:
...
try {
employeeService.updateEmployeePartner(employeeId, partner);
LOG.info("partner details updated for partner id {}", employeeId);
result = new ResponseEntity<>(partner.getId(), HttpStatus.OK);
} catch (EmployeePartnerNotFoundException ex) {
LOG.error(ex.getMessage() + " employee id: ", employeeId);
errorResponse = new ErrorResponse("500", ex.getMessage());
} catch (ReadOperationDeniedException ex) {
LOG.error("User doesn't have permissions to update employee's {} details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("403", "User doesn't have permissions to update employee's details");
} catch (Exception ex) {
LOG.error("something went wrong while updating employee's {} partner details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("500", "unspecified server error");
} finally {
result = (result != null) ? result : new ResponseEntity<>(errorResponse, HttpStatus.I_AM_A_TEAPOT); // should be INTERNAL_SERVER_ERROR
}
...
Another method is almost identical, apart for this change:
employeeService.updateEmployeePartner(employeeId, partner); =>
employeeService.createEmployeePartner(employeeId, partner);
and catching EmployeePartnerAlreadyExistsException in that block.
Now, to reduce code duplication, I want to group all Error handling code in one place (method), so I replaced the above code with the following
...
try {
employeeService.updateEmployeePartner(employeeId, partner);
LOG.info("partner details updated for partner id {}", employeeId);
result = new ResponseEntity<>(partner.getId(), HttpStatus.OK);
} catch (Exception ex) {
errorResponse = processException(ex, employeeId, "update");
} finally {
result = (result != null) ? result : new ResponseEntity<>(errorResponse, HttpStatus.I_AM_A_TEAPOT); // should be INTERNAL_SERVER_ERROR
}
...
private ErrorResponse processException(Exception ex, Long employeeId, String operation) {
ErrorResponse errorResponse;
if (ex.getClass().equals(EmployeePartnerNotFoundException.class) ||
ex.getClass().equals(EmployeePartnerExistsException.class)) {
LOG.error(ex.getMessage() + " employee id: ", employeeId);
errorResponse = new ErrorResponse("500", ex.getMessage());
} else if (ex.getClass().isInstance(ReadOperationDeniedException.class)) {
LOG.error("User doesn't have permissions to " + operation + " employee's {} details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("403", "User doesn't have permissions to " + operation + " employee's details");
} else { // Exception
LOG.error("something went wrong while trying to " + operation + " employee's {} partner details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("500", "unspecified server error");
}
return errorResponse;
}
Is that a good enough approach or are there any patterns to handle exceptions in the above scenario by outsourcing the handling to a separate method/class?
Since it's a spring application, I also consider using Spring exception handlers, as in:
#ExceptionHandler(Exception.class)
, but that will only cover part of my requirements.
Use #ControllerAdvice with your custom ErrorResponse and each Handler for seprate exceptions. Refer Custom error response Spring
Sample code:
#ControllerAdvice
public class GlobalExceptionHandlers {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandlers.class);
/***************** User Defined Exceptions *************************/
#ExceptionHandler({ EmployeePartnerNotFoundException.class })
public ResponseEntity<Object> handleEmployeePartnerNotFoundException(EmployeePartnerNotFoundException ex) {
logger.error("EmployeePartnerNotFoundException : ", ex);
ErrorResponse errorResponse = new ErrorResponse("500", ex.getMessage());
return new ResponseEntity<Object>(errorResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
// other exception handlers
}
That's what I ended up doing in the end, along with the reply by Sangam:
Individual Exception handlers worked quite well; Do note there is no need to put these in separate class.
But I still wonder if there is a similar pattern where the application is not Spring MVC?
public ResponseEntity<?> updatePartnerDetails(#PathVariable("employeeId") Long employeeId,
#RequestBody PersonDetails partnerDto) {
LOG.info("Updating partner details for employee {}, partner details {}", employeeId, partnerDto);
validateRequestValues(partnerDto);
// Try-catches were around this call
Person partner = PersonMapper.fromPersonDetails(partnerDto);
employeeService.updateEmployeePartner(employeeId, partner);
LOG.info("partner details updated for partner id {}", employeeId);
return new ResponseEntity<>(partner.getId(), HttpStatus.OK);
}
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: BAD_REQUEST
#ExceptionHandler({EmployeePartnerExistsException.class, EmployeePartnerNotFoundException.class})
public ResponseEntity<?> employeePartnerError(Exception ex) {
LOG.error(ex.getMessage());
return new ResponseEntity<Object>(new ErrorResponse(400, ex.getMessage()), HttpStatus.OK);
}
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: BAD_REQUEST
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<?> validationError(Exception ex) {
LOG.error(ex.getMessage());
return new ResponseEntity<Object>(new ErrorResponse(400, ex.getMessage()), HttpStatus.OK);
}
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: FORBIDDEN
#ExceptionHandler(ReadOperationDeniedException.class)
public ResponseEntity<?> forbidden(Exception ex) {
LOG.error("User doesn't have permissions to amend employee's details");
return new ResponseEntity<Object>(new ErrorResponse(403, "User doesn't have permissions to amend employee's details"), HttpStatus.OK);
}
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: INTERNAL_SERVER_ERROR
#ExceptionHandler(Exception.class)
public ResponseEntity<?> unspecifiedError(Exception ex) {
LOG.error("User doesn't have permissions to amend employee's details");
return new ResponseEntity<Object>(new ErrorResponse(500, "Something went wrong while editing employee's details"), HttpStatus.OK);
}
Related
I want to call async rest endpoints with Feign client and have done the below changes.
When calling it the CompletableFuture.get() doesn't complete.
The while keeps looping...
while(!combinedFuture.isDone()) { log.info("useraccount - waiting for combinedFuture 2: " + request.toString()); }
Interface to call the API:
#FeignClient(value = "clientUser", url = "http://localhost:8898/springboot", fallback = UserFallback.class)
public interface User {
#RequestMapping(method = RequestMethod.GET, value = "/user/", produces = "application/json")
#Async
CompletableFuture<UserInfo> findUserInfo(#RequestHeader(value = "Authorization", required = true) String authorizationHeader);
}
Controller method:
#PostMapping(value = "/springboot/useraccount/", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> userAccount(#requestbody #Valid AuthRequest request) {
log.info("useraccount - request received with request body: " + request.toString());
try {
if (Strings.isBlank(request.getUsername()) || Strings.isBlank(request.getPassword())) {
throw new BadRequestException("invalid username or password");
}
String token = authorisationService.obtainAuthToken(request.getUsername(), request.getPassword());
CompletableFuture<UserInfo> userInfo = clientUser.findUserInfo(token);
CompletableFuture<UserAccountInfo> userAccountInfo = clientAccount.findAccountInfo(token);
CompletableFuture<Void> combinedFuture
= CompletableFuture.allOf(userInfo, userAccountInfo);
while(!combinedFuture.isDone()) {
log.info("useraccount - waiting for combinedFuture 2: " + request.toString());
}
Optional<UserAccountResponse> userAccountResponse = userAccountService.getAccountInfo(
userAccountInfo.get(), userInfo.get()
);
if (userAccountResponse.isEmpty()) {
throw new BadCredentialsException("Bad Credentials");
}
return ResponseEntity.ok().body(userAccountResponse);
} catch (BadCredentialsException | UnAuthorizedException ex) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (BadRequestException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
} catch (ExecutionException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} catch (InterruptedException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
(update) Have changed a bit to use CompletableFuture.supplyAsync
but now the object is always null...
#Service
public class AccountService {
#Autowired
Account accountClient;
#Async
public Optional<UserAccountInfo> getAccountInfo(String token) {
return Optional.of(accountClient.findAccountInfo(token));
}
}
Managed to solve it like this:
#Async
public CompletableFuture<UserAccountInfo> getAccountInfo(String token) {
try {
System.out.println(
"Current Thread account Name: "
+ Thread.currentThread().getName());
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(accountClient.findAccountInfo(token).getBody());
}
The same for the userInfo service. Then on the controller:
CompletableFuture<UserAccountInfo> userAccountInfo = accountService.getAccountInfo(token);
CompletableFuture<UserInfo> userInfo = userService.getUserInfo(token);
Optional<UserAccountResponse> userAccountResponse = userAccountService.getAccountInfo(
userAccountInfo.get(),userInfo.get()
);
So that means both services will start running each in a new thread while the main thread continues ran until find the first .get().
By doing it like that the maximum waiting time to finish is the time of the thread that takes more time and not the sum of both as it happens if it is a synchronous.
Thanks!
How to perform rollback in Spring Data JPA for the following scenario?
Transactional
#Override
public Employee saveEmployee(EmployeeDto dto) {
// check if EmployeeId and Department Id is present
Employee employee = this.getByEmployeeId(dto);
Department department = this.getByDepartmentId(dto);
Employee employee = convertToEntity(dto, employee, department);
employee.setEmployees(Arrays.asList(employee));
department.setEmployees(Arrays.asList(employee));
try {
employee = employeeRepository.save(employee); //line-11
} catch (DataIntegrityViolationException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "ConstraintViolationException", e.getCause());
} catch (Exception ex) {
throw new InternalServerException(HttpStatus.INTERNAL_SERVER_ERROR, env.getProperty(IConst.ERROR_DB_EXCEPTION), ex);
}
EmployeeEmployeeDepartment r = new EmployeeEmployeeDepartment();
r.setId(new EmployeeDepartmentPK());
r.setEmployee(employee);
r.setDepartment(department);
r.setEmployee(employee);
try {
compositeRepository.save(r); //line-22
}catch (DataIntegrityViolationException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "ConstraintViolationException", e.getCause());
}
catch (Exception ex) {
throw new InternalServerException(HttpStatus.INTERNAL_SERVER_ERROR, env.getProperty(IConst.ERROR_DB_EXCEPTION), ex);
}
return employee;
}
How to roll back line-11 if line-22 fails?
1) If ResponseStatusException and InternalServerException are both RuntimeExceptions then you do not need to do anything as Spring by default rolls back the entire transaction on any RTE.
2) Just keep in mind that invoking save() and eventually persist() on entityManager does not cause any physical update on the DB until the transaction commits. These methods simply register an entity in the Persistence Context.
Use "rollbackFor"
#Transactional(rollbackFor = DataIntegrityViolationException.class)
Multiple exception:
#Transactional(rollbackFor = { ResponseStatusException.class, InternalServerException.class })
I have Hystrix commands in my class which I need to test. I am able to mock all the code except the fallback. To execute the fallback, I need to make my hystrix wrapped method to throw a timeout exception. I don't get how to do that. Can someone help me with it? I tried opening the circuit with #Enablecircuitbreaker on the test class, but that didn't invoke any Hystrix exception :(
#Mock
private MDMConnectorService service;
#InjectMocks
private AIAUtilities aiaUtilities;
#Test
public void testFetchCustomerAccountDetailsHystrixTimeoutException() throws Exception {
try {
ConfigurationManager.getConfigInstance()
.setProperty("hystrix.command.AIAClientCommand.circuitBreaker.forceOpen", "true");
Mockito.when(service.fetchCustomerAccount(any(GetCustomerAccountType.class))).thenReturn(getTestAIARecord());
GetCustomerAccountResponseType responseType = aiaUtilities
.fetchCustomerAccountDetails(accountNumber);
Assert.assertFalse(true);// if the flow came here, the test case has failed
} catch (Exception ex) {
if (ex instanceof DataAccessException) {
assertEquals(Constants.ERRCODE_AIA_QUERY_TIMED_OUT,
((DataAccessException) ex).getErrorCode());
} else {
throw ex;
}
}
finally {
ConfigurationManager.getConfigInstance()
.setProperty("hystrix.command.AIAClientCommand.circuitBreaker.forceOpen", "false");
}
}
In this test, the command wrapped by hystrix is being called at
GetCustomerAccountResponseType responseType = aiaUtilities
.fetchCustomerAccountDetails(accountNumber);
The code of AIAUtilities having the hystrix command and corresponding fallback is
#HystrixCommand(commandKey = "AIAClientCommand", fallbackMethod = "aiaClientCommandFallback")
public GetCustomerAccountResponseType fetchCustomerAccountDetails(String accountNumber)
throws DataAccessException {
GetCustomerAccountResponseType response;
try {
if (generalUtil.isObjectEmpty(authHeader)) {
authHeader = iamUtilities.createAuthHeaderAndRenewOfflineTicket();
}
factory = getFactory();
request = getRequest();
accountNumberType = getAccountNumberType();
accountNumberType.setValue(accountNumber);
request.setCustomerAccountNumber(accountNumberType);
request.setSourceId(Constants.VAL_QUICKBASE_SOURCE_AIA);
serviceClass = getServiceClass();
service = getService();
provider = getProvider();;
provider.getRequestContext().put("Authorization", authHeader);
provider.getRequestContext().replace("SOAPAction", "fetchCustomerAccount");
provider.getRequestContext().put("Content-Type", "text/xml");
response = service.fetchCustomerAccount(request);
} catch (DataAccessException e) {
throw e;
}
catch (Exception e) {
if(e instanceof HystrixRuntimeException && e.getCause() instanceof TimeoutException) {
DataAccessException dataAccessException = (DataAccessException) ((HystrixRuntimeException) e)
.getFallbackException().getCause();
throw new DataAccessException(dataAccessException.getErrorCode(),
"Exception in validateLicense.fetchCustomerAccountDetails::" + e.getMessage(),e);
}
else
throw new DataAccessException(Constants.ERRCODE_AIA_EXCEPTION,
"Exception in validateLicense.fetchCustomerAccountDetails:::" + e.toString(), e);
}
return response;
}
private GetCustomerAccountResponseType aiaClientCommandFallback(String accountNumber, Throwable e)
throws DataAccessException {
logger.error("Inside AIAClientCommandFallback : Error is ::" + e.toString());
if(e instanceof HystrixTimeoutException)
throw new DataAccessException(Constants.ERRCODE_AIA_QUERY_TIMED_OUT,
"Exception in AIAClientCommandFallback::" + e.toString(),e);
else if(e instanceof DataAccessException)
throw (DataAccessException)e;
else
throw new DataAccessException(Constants.ERRCODE_AIA_EXCEPTION,
"Inside AIAClientCommandFallback : Error is ::" + e.toString(), e);
}
Instead of returning something in your mocked fetchCustomerAccount just throw an Exception there via thenThrow:
Mockito.when(service.fetchCustomerAccount(any(GetCustomerAccountType.class))).thenThrow(new RuntimeException("Timeout"));
My team implemented Sonar code coverage for my project and one the method in class complaining about the error 'Either remove this useless object instantiation of class "ResponseEntity" or use it '. If i remove the line it is complaining it will work. But i want to handle that error as well.
Any suggestions how this to be handled will be appreciated
#RequestMapping(value = "/**/identity", method = RequestMethod.POST)
public ResponseEntity<String> createIdentity(#RequestBody #NotNull Heartbeat heartbeat) {
//Validate
if (StringUtils.isEmpty(heartbeat.getHostname())
|| StringUtils.isEmpty(heartbeat.getEnvironment())
|| StringUtils.isEmpty(heartbeat.getProcessSignature())) {
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
try {
byte[] encodedValue = identityService.createIdentity(heartbeat.getHostname(), heartbeat.getEnvironment(),
heartbeat.getProcessSignature());
return ResponseEntity.ok(new String(encodedValue));
} catch (BadPaddingException | IllegalBlockSizeException e) {
log.error("Unable to create entity for the request", e);
new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); // Sonar Complaint
}
return ResponseEntity.ok().build();
}
The problem is you are not returning the response that you think you are in Error scenario. You can solve this by correctly returning the INTERNAL_SERVER_ERROR response.
Sonar is just pointing out that probable flaw.
#RequestMapping(value = "/**/identity", method = RequestMethod.POST)
public ResponseEntity<String> createIdentity(#RequestBody #NotNull Heartbeat heartbeat) {
//Validate
if (StringUtils.isEmpty(heartbeat.getHostname())
|| StringUtils.isEmpty(heartbeat.getEnvironment())
|| StringUtils.isEmpty(heartbeat.getProcessSignature())) {
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
try {
byte[] encodedValue = identityService.createIdentity(heartbeat.getHostname(), heartbeat.getEnvironment(),
heartbeat.getProcessSignature());
return ResponseEntity.ok(new String(encodedValue));
} catch (BadPaddingException | IllegalBlockSizeException e) {
log.error("Unable to create entity for the request", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return ResponseEntity.ok().build();
}
I am writing a chat application using java websocket, now I need to save chat history, so when calling onMessage method I create my object, set its property and save it to data base,but with no reason It doesn't recieve to persist part, here is my code:
#OnMessage
public void onMessage(final Session session,
final ChatMessageBean chatMessage) {
String sender = (String) session.getUserProperties().get("user");
try {
for (Session s : session.getOpenSessions()) {
if (s.isOpen()
&& ((chatMessage.getReceiver()).equals(s
.getUserProperties().get("user")) || sender
.equals(s.getUserProperties().get("user")))) {
s.getBasicRemote().sendObject(chatMessage);
System.err.println("chatMessage is : " + chatMessage);
System.err.println("user is : "
+ s.getUserProperties().get("user"));
}
}
} catch (IOException | EncodeException e) {
log.log(Level.WARNING, "onMessage failed", e);
}
ChatMessage chatConv = new ChatMessage();
chatConv.setMessage("hihii");
chatConv.setRecievedDate(new Date());
chatConv.setSeenStaus(true);
chatConv.setSenderId("7923");
chatConv.setReciever("702");
chatCrud.addChat(chatConv);
}
and my addChat method on chatCrud Class:
public class ChatCrud {
#Inject
EntityManager em;
public String addChat(ChatMessage chat) {
try{
em.getTransaction().begin();
em.persist(chat);
em.getTransaction().commit();
}catch(Exception e){
System.out.println("!!!!!! "+e);
}
return "OK";
}
}
I changed my method to catch exception, The sysout result was:
!!!!!! org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.RequestScoped
Is onMessage method the wrong place to save history of chat?