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.
Related
I want to make a REST API call which return a boolean value as a part of the custom annotation.
Sample Code :
**#CustomAnnotation
public String myMethod(){
// my implementation
}**
Only if the boolean value from the REST call is true, the method "myMethod must get triggered and implementation should happen else throw an exception similar to #NotNull .
I was wondering if this is possible, if yes someone please help me out.
you can create a simple custom annotation without worrying about code to call rest.
For executing rest call code ->
Read about how to apply aspect oriented programming.
Basically using aop (apsect oriented programming), you can write a code such that for any method which is annotated with your custom annotation, you wish to execute some piece of code before calling your method.
To do this in spring
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#interface CustomAnnotation {
String value() default "";
}
#Pointcut(value = "#annotation(CustomAnnotation)") // full path to CustomAnnotation class
public void abc() {
}
#Around("abc()")
public Object executeSomePieceOfCode(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("this executes before calling method");
// YOUR CODE TO CALL REST API
boolean responseFromRestCall = true; // this flag is set based on response from rest call
if(responseFromRestCall) {
// this excutes your method
Object obj = joinPoint.proceed();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CustomAnnotation myAnnotation = method.getAnnotation(CustomAnnotation.class);
String value = myAnnotation.value();
System.out.println("value : + " + value);
return obj;
} else {
// currently throwing RuntimeException. You can throw any other custom exception.
throw new RuntimeException();
}
}
I am writing a unit test for my below code
public class Class1 {
protected void execute(String a, String b) {
try{
process(a,b);
}
catch(Exception E){
Class2.write(e,Class1.class.getSimpleName())
}
}
private void process(String a, String b) {
validate(a,b);
// Doing some processing on a and b values
}
private void validate (String a, String b) {
if(a==null || a.isEmpty() || b==null || b.isEmpty())
throw new IllegalArgumentException("Input value cannot be null or empty");
}
}
For the above code, I am trying to write a UT which covers the exception use case. Below is my UT code,
#Test
public void test1(){
try {
PowerMockito.mockStatic(Class2.class);
PowerMockito.when(Class2.class, "write", Mockito.anyObject(), Mockito.anyString())
.thenCallRealMethod();
Class1 class1 = new Class1();
Class2.write(new IllegalArgumentException("Input value cannot be null or empty"),Class1.class.getSimpleClassName());
PowerMockito.verifyStatic(Class2.class, VerificationModeFactory.times(1));
class1.execute(Mockito.anyString(),Mockito.anyString());
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
I am getting the below exception when I execute the above test
Argument(s) are different! Wanted:
Class2.write{
java.lang.IllegalArgumentException:Input value cannot be null or empty,
Class1
}
Actual invocation has different arguments:
Class2.write{
java.lang.IllegalArgumentException:Input value cannot be null or empty,
Class1
}
Can someone please help me on resolving this issue?
I really appreciate your help and time
Thanks in Advance
Your Problem:
IllegalArgumentException does not use the string message for equality. It would be safer to test the string message or the class type. I would prefer that the test detect the type rather than the message, as the string message should not be used for control flow, it is an implementation detail.
System.out.println(Objects.equals(
new IllegalArgumentException(),
new IllegalArgumentException()));
// false
System.out.println(Objects.equals(
new IllegalArgumentException().getClass(),
new IllegalArgumentException().getClass()));
// true
So to mock this I would use matchers:
any(IllegalArgumentException.class), eq(Class1.class.getSimpleName())
Issues with your design:
I'm going to end with an argument against how this code is structured, being that it is not built around dependency injection. Rather than calling the static method Class2::write, you could be calling an instance method.
For example, create the interface:
public interface Writer {
void write(Exception e, String source);
}
You can now refactor the class to provide two ctors, one that accepts any writer, and one that defaults to Class2.
public class Class1 {
private final Writer writer;
public Class1() {
this(Class2::write);
}
public Class1(Writer writer) {
this.writer = writer;
}
protected void execute(String a, String b) {
try {
process(a,b);
}
catch (Exception E) {
writer.write(e, Class1.class.getSimpleName());
}
}
...
}
Using this strategy you can now simply create an instance mock of Writer. This avoids having to mock as static method which changes the bytecode of your application, and also make your class more flexible as it can support many different writer implementations now. Anything that is modifying the bytecode of the application should be used very sparingly, such as replacing static method calls, does not truly validate the runtime execution of your code.
In my opinion, the majority of the PowerMockito/PowerMock only help verify code which was not built with testability / flexibility in mind. You shouldn't need to use anything outside of the Mockito/EasyMock tool-set for well structured code. There are some exceptions but the tool-set should be used very sparingly.
I am having troubles while trying to refactor exception handling logic in an helper class.
My code uses a repository which accesses a database and might throw the custom exception RepositoryException. If such exception is thrown by the repository, I want my code to catch it and set an error label in the graphical user interface (view):
... // more code
try {
existingCourse = repository.findByTitle(course.getTitle()); // <- throws RepositoryException
} catch (RepositoryException e) {
view.showError(e.getMessage(), course);
return;
}
... // some more code
The point is that this code is repeated several times and I would prefer to have it refactored in an helper class.
This is what I came up to after some experiments:
A custom FunctionalInterface called ThrowingSupplier, which represent the code that throws the exception.
A TransactionManager helper class, with a catcher methods that accepts a ThrowingSupplier
This is the related code (BaseEntity is just a base class for entities in my domain, as you might guess):
// ThrowingSupplier.java
#FunctionalInterface
public interface ThrowingSupplier<T extends BaseEntity> {
T get() throws RepositoryException;
}
/* ------------------------------------------------------ */
// ExceptionManager.java
public final class ExceptionManager<T extends BaseEntity> {
private T result;
private String exceptionMessage;
ExceptionManager() {
}
public boolean catcher(ThrowingSupplier<T> supplier) {
try {
clearResult();
clearExceptionMessage();
result = supplier.get();
return true;
} catch (RepositoryException e) {
exceptionMessage = e.getMessage();
}
return false;
}
// public getters and 'clearers' for attributes
...
}
And this is how I am using this now:
...
em = new ExceptionManager();
... // more code
if (!em.catcher(() -> repository.findByTitle(course.getTitle()))) {
view.showError(em.getExceptionMessage(), course);
return;
}
existingCourse = em.getResult();
... // some more code
Now it seems to me that this does not give any advantages with respect to using directly the try catch in every repository invocation. This is mainly because I need both the return value of the repository method and a way to tell the caller if the repository call has been successful. As a variation I tried to add the showError call inside catcher, but then I must pass view and entity in every invocation of catcher, which I do not like very much as it makes the code less readable.
Is there another way to accomplish this in an elegant manner or it is better to leave the try catch in every call to the repository? Also, what is the standard way to deal with this problem?
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);
I have method in java class :
#Context
UriInfo uriInfo;
public void processRequest(#QueryParam ("userId") #DefaultValue("") String userId)
{
String baseURI = uriInfo.getBaseUri().toString();
if(userId == null)
{
//UserIdNotFoundException is my custom exception which extends Exceptition
throw new UserIdNotFoundException();
}
}
When I'm junit testing the above method expecting for UserIdNotFoundException when userId parameter is Null, I get the following Assertion error : expected an instance of UserIdNotFoundException but <java.lang.NullPointerException> is java.lang.NullPointerException.
#Test
public void testProcessRequest_throws_UserIdNotFoundException()
{
expectedException.expect(UserIdNotFoundException.class);
processRequest(null);
}
My custom exception class :
public class UserIdNotFoundException extends Exception
{
public UserIdNotFoundException()
{
}
public UserIdNotFoundException(String message)
{
super(message);
}
}
I prefer the annotation:
#Test(expected = UserIdNotFoundException.class)
public void testProcessRequest_throws_UserIdNotFoundException() {
processRequest(null);
}
The problem might be that your processRequest implementation might be hitting the NPE before you have a chance to check for user id.
This is a good thing: Your test shows that the implementation does not meet your requirement. You can fix it forever now.
This is what TDD is good for.
You have to write custom Exception class this example might help you.
sample code:
class UserIdNotFoundException extends Exception{
UserIdNotFoundException (String s){
super(s);
}
}
Test Exception:
public void processRequest(String userId)
{
if(userId == null)
{
//UserIdNotFoundException is my custom exception which extends Exception
throw new UserIdNotFoundException("SOME MESSAGE");
}
}
remove default constructor from your exception class, JVM implicitly create it for you/
You probably did not set uriInfo with a value and you're calling a method on a null value. Are you sure you're test is setup to give a value to uriInfo ? Or getBaseUri() might be returning null and calling toString() on it might throw the NullPointerException. This would be done by inspecting the return value of getBaseUri() in a debugger.
Normally you can either run your test with a config with beans for your test or you can add setter to set the value in your class of test to mock it or give a value in test. This should help avoid NullPointerException.
Either way you should always do the fail verification before doing any real work in a method.