How to properly throw MethodArgumentNotValidException - java

I'm trying to get the same result as when I use #Valid in object parameter from a Controller. When the object is invalid an exception (MethodArgumentNotValidException) is throw by my ExceptionHandlerController who has #RestControllerAdvice.
In my case I want to validate an object, but I only can validate it in service layer. The object have bean validation annotations, so I'm trying to programmatically throw MethodArgumentNotValidException for my ExceptionHandlerController handle it, but I'm not having success.
So far I have this:
private void verifyCard(CardRequest card) {
BeanPropertyBindingResult result = new BeanPropertyBindingResult(card, "card");
SpringValidatorAdapter adapter = new SpringValidatorAdapter(this.validator);
adapter.validate(card, result);
if (result.hasErrors()) {
try {
throw new MethodArgumentNotValidException(null, result);
} catch (MethodArgumentNotValidException e) {
e.printStackTrace();
}
}
}
The first parameter is from type MethodParameter and I'm not been able to create this object. Is it the best way to handle my problem?
EDIT 1:
I can't remove the try/catch block. When I remove it I get compile error. How to work around?

You have already handled it by the catch block, you should remove try-catch to your global handler catch it.
then specify the method like below
private void verifyCard(CardRequest card) throws MethodArgumentNotValidException

MethodArgumentNotValidException is a subclass of Exception. This means that it's "checked": To throw it out of your verifyCard(..) method, you have to declare that verifyCard(..) can throw it:
private void verifyCard(CardRequest card) throws MethodArgumentNotValidException {
// your code
}

If you have lombok dependency in your project, you can also fake compiler by using #SneakyThrows annotation.
https://projectlombok.org/features/SneakyThrows

throw new MethodArgumentNotValidException(null, result);
Above constructor will not work as method parameter is necessary.
Valid constructor (reference) is:
MethodArgumentNotValidException(MethodParameter parameter, BindingResult bindingResult);
Hence, in your case:
throw new MethodArgumentNotValidException(new MethodParameter(
this.getClass().getDeclaredMethod("verifyCard", YourClassName.class), 0), errors);

Related

Mock in java exception

I have this catch statement:
catch (NotFoundException ex) {
ex.getError().setTitle(NOT_FOUND);
throw new NotFoundException(resource, id, ex.getError());
}
How can I mock this exception? I've tried this
when(service
.filter(eq(any()), eq(any()), eq(any())))
.thenThrow(new NotFoundException(anyString(), anyString()));`
But it gives me a null exception error because of this line:
ex.getError().setTitle(NOT_FOUND);
The constructor is:
public NotFoundException(String resource, String id, Error error) {
this.resource = resource;
this.ids = Collections.singletonList(id);
this.error = error;
}
And I can't get the exception variable to set the title, or find an way to mock it.
Thanks for you help!
.thenThrow(new NotFoundException(anyString(), anyString()));
This isn't allowed: anyString() only stands directly in for the call in when and verify. In your call to filter, simply use any() rather than eq(any()), but you're otherwise using matchers in the correct place.
Furthermore, it looks like your system-under-test assumes that ex.getError() is non-null; it is likely that you'll need to pass in a useful Error instance as constructor parameter into the NotFoundException you create.
.thenThrow(new NotFoundException("foo", "bar", new Error(/* ... */)))
Naturally, if your Error is difficult to create or work with, you might use a mock(Error.class) instead.

How to handle errors in a Spring-MVC project without if else?

I can’t understand how to handle the following error:
In the class CustomerService I delete the customer by id, and if such an id does not exist, then an error must be thrown! How can you do without an if else construct?
CustomerService:
// Delete customer
public void deleteCustomer(Long id){
Customer customer = customerRepository.getByIdAndUserRole(id, "customer");
customerRepository.delete(customer);
}
CustomerController:
// DELETE MAPPING
//
// Delete customer with ID
#DeleteMapping("/customers/{id}")
void deleteCustomer(#PathVariable Long id) {
customerService.deleteCustomer(id);
}
Try to use Controller Advice. Whenever a exception occur it will directly handled by the handler. No if/else or try/catch blocks will be required.
1) Create a class CustomerControllerHandler, annotate with #ControllerAdvice.
2) Now create methods with arguments having the type of Exception.
3) The methods will return the JSON/POJO/void you want.
4) Annotate the methods with #ExceptionHandler(Exception.class) and
#ResponseStatus(HttpStatus.BAD_REQUEST),
#ControllerAdvice
public class CustomerControllerHandler {
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void processException(Exception ex) {
}
}
You can try using this instead. It's the deleteById method for a CrudRepository (hope you're using that) and it throws IllegalArgumentException if it can't find a customer.
I assumed that with "error" you meant "exception" and then in the controller you can surround with a try-catch block like that:
try{
customerService.deleteCustomer(id);
} catch (IllegalArgumentException e) {
log.error("No customer id exists!", e);
// if you have no logger, then use System.out.println() at least
}
If you wanted instead to return an error to the caller, then change the data type from void to HttpResponse<String> and when catching an exception you can return HttpResponse<>("No customer exists with that id!", HTTP.BAD_REQUEST). Now the caller will get a 400 - bad request.
A nicer approach would be to catch the exception in the service itself and return a boolean to the controller (true if customer is deleted and false if couldn't delete / couldn't find one).
if you want to throw error then you will have to check a condition, that is there will be an if statement, but not necessarily an else is needed.
For instance, you can check response of delete and throw error according to below one.
if (deleteCount == 0) {
//throw error here
}

Spring AOP and annotation to check parameter before method execution

I have to throw an exception if a method parameter is a particular value.
The aim is to lock all the method that work with the specific value so I thought to use Spring AOP but I am new with it.
My problem is retrieve the value of method parameter, I create this sample:
Annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TestAOPAnnotation {
}
AOP class
#Component
#Aspect
public class TestAOP {
#Before("#annotation(TestAOPAnnotation)")
public void logAOP(){
//Make some operations with database and throw exception in a specific case
throw new RuntimeException();
}
}
The method where I use the annotation
#Override
#TestAOPAnnotation
public List<Animals> findByName(String name) throws QueryException {
try{
return animalsRepository.findByName(name);
}catch(Exception e){
throw new QueryException(e);
}
}
and where I catch the exception
#Override
#RequestMapping(value="/test/{name}", method = RequestMethod.GET)
public #ResponseBody List<Animals> findByName(#PathVariable String name){
try{
return databaseAnimalsServices.findByName(name);
}catch(QueryException e){
return null;
}catch(Exception e){
//CATCH AOP EXCEPTION
List<Animals> list = new ArrayList<Animals>();
list.add(new Animals("AOP", "exception", "test"));
return list;
}
}
How can I get the name parameter? I may use another annotation on parameter (or only this annotation) but I don't know how. Can you help me?
EDIT
To catch parameter annotation I may use:
#Before("execution(* *(#Param (*),..))")
but it works only if I know the parameters order instead I need only the annotated parameter.
Otherwise , until now, the best solution is
#Before("#annotation(TestAOPAnnotation) && args(name,..)")
public void logAOP(String name){
System.out.println(name);
throw new RuntimeException("error");
}
but the parameter must be the fist in the signature
You could use an #Around advice which has access to the invocation data.
#Around("#annotation(TestAOPAnnotation)")
public Object logAOP(ProceedingJoinPoint aPoint) throws Throwable {
// First, implement your checking logic
// aPoint.getArgs() may be inspected here ...
if (...) {
throw new RuntimeException(...);
}
// now, actually proceed with the method call
return aPoint.proceed();
}
getArgs() gives you access to the real arguments passed to the method.
You can get the calling arguments from the joinPoint:
#Around(.....)
public Object check(ProceedingJoinPoint joinPoint) {
for (Object object : joinPoint.getArgs()) {
.... add your checks here.
}
return joinPoint.proceed();
}
Note you can not easily get the name of the parameter from the joinPoint (like you can in .NET) but you could check on type and value.
See https://blog.espenberntsen.net/2010/03/20/aspectj-cheat-sheet/ for valid execution patterns.

Can I return an Object of custom type using afterThrowing() method in spring?

I have a situation where I want to mock a web service call. The approach I am using is if the service is down, it throws an exception. I intent to use that exception using ThrowAdvice AOP in spring and want to suppress the exception and replace the response object with my dummy object. How can I do so?
I suggest you to use Around advice to intercept the method. This advice will give you absolute control. In this advice you can catch the exception and return the require response
#Around("execution(abc.example.*Service.*(..))")
public ResultType execute(ProceedingJoinPoint p) {
ResultType result = null;
try {
result = (ResultType) p.proceed();
return result;
} catch (Throwable t) {}
// Create dummy result
return result;
}
ResultType can be any type, but be sure not intercept service which are not returning same type or subtype

How to resolve findbug issue: Null passed for nonnull parameter

I'm getting the following findbugs error:
"Method call passes null for nonnull parameter : Null passed for nonnull parameter of getApiStatus(ApiResponse)"
If the apiResponse is null in the CallApi method (not shown here for brevity's sake), it simply throws an exception that is caught in handleApiException and thrown again if we can't do anything else about the exception.
There is no way that a null value for apiResponse could be passed into getApiStatus() method at the botton of this code snippit. How can I tell findbugs that this is the case without doing yet another null check on top of the null check that is done in the apiService.CallApi method? I've tried using the NonNull annotation, but that didn't resolve the issue. Here's the code in question:
ApiResponse apiResponse = null;
try {
apiResponse = apiService.CallApi(apiURL, requestObject);
}
catch (ApiException ex) {
handleApiException(ex);
}
boolean apiStatus = getApiStatus(apiResponse);
Any Ideas?
My suggestion would be to NOT handle the exception, but to set this method as throws ApiException. And then handle it higher up the chain. If your code gets an exeption in that try block, then handles the exception in the catch, then apiResponse can easily be null. And will then go on to try the getApiStatus method, hence passing in a null.
public void yourMethod() throws ApiException {
ApiResponse apiResponse = apiService.CallApi(apiURL, requestObject);
boolean apiStatus = getApiStatus(apiResponse);
// Whatever else you need to do here.
}
Your only other option is to put the apiStatus call below the apiResponse one inside the try block, like so:
ApiResponse apiResponse = null;
try {
apiResponse = apiService.CallApi(apiURL, requestObject);
boolean apiStatus = getApiStatus(apiResponse);
} catch (ApiException ex) {
handleApiException(ex);
}
Or, as you say, do a null check before calling getApiStatus, but that's not as preferable as the options above.
In your code, the getApiStatus(apiResponse) will be called regardless of the ApiException occurring or not.
You should have this instead:
try {
ApiResponse apiResponse = apiService.CallApi(apiURL, requestObject);
// line bellow will not execute if CallApi throws ApiException
boolean apiStatus = getApiStatus(apiResponse);
}
catch (ApiException ex) {
handleApiException(ex);
}
// lines bellow will execute after try-catch block above
// regardless of the ApiException occurring or not
If CallApi throws an exception, then it will be handled and control will continue to getApiStatus, without apiResponse ever being assigned anything other than the initial null.

Categories