How to unit test Lombok #SneakyThrows annotation existence under method - java

#Test
void annotatedClass() throws NoSuchMethodException {
Class<? extends service> aClass = underTest.getClass();
Method method = aClass.getDeclaredMethod("create", param.class);
method.setAccessible(true);
SneakyThrows sneakyThrows = method.getAnnotation(SneakyThrows.class);
Assertions.assertNotNull(sneakyThrows);
}
But assert is Null and private method annotated with #SneakyThrows
IDEA talk:
Annotation 'SneakyThrows.class' is not retained for reflective access
Can we beat annotation retention and be able to test it somehow?

As far as I understand Lombok's #SneakyThrows has retention SOURCE, so it's not preserved in the compiled code. See the sources:
#Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
#Retention(RetentionPolicy.SOURCE)
public #interface SneakyThrows {
/** #return The exception type(s) you want to sneakily throw onward. */
Class<? extends Throwable>[] value() default java.lang.Throwable.class;
//The fully qualified name is used for java.lang.Throwable in the parameter only. This works around a bug in javac:
// presence of an annotation processor throws off the type resolver for some reason.
}
Here is a simple way to check whether #SneakyThrows is present over a method:
class MyTest {
#Test
void testSneakyThrows() throws NoSuchMethodException {
Class<IOException> exceptionType = IOException.class;
assertThrows(exceptionType, this::myMethod);
assertFalse(Arrays.asList(getClass().getMethod("myMethod").getExceptionTypes()).contains(exceptionType));
}
#SneakyThrows
public void myMethod() {
throw new IOException();
}
}

Related

AOP and annotation 'inheritance'

let's consider the following situation.
#interface LoggedMethodInvocation{}
#LoggedMethodInvocation
#interface MonitoredMethodInvocation{}
I would like the #MonitoredMethodInvocation annotation implying the #LoggedMethodInvocation annotation.
class LoggingAOPConfig {
#Pointcut("#annotation(LoggedMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object logMethodInvocation(ProceedingJoinPoint pjp) throws Throwable {
// log the method invocation...
}
}
class MonitoringAOPConfig {
#Pointcut("#annotation(MonitoredMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object monitorResponseTime(ProceedingJoinPoint pjp) throws Throwable {
// add some meters to the method invocation
}
}
Now I would like to introduce some method, which shall be both monitored and logged. And I would like to annotate the method only with one annotation, namely #MonitoredMethodInvocation.
class SomeService {
#MonitoredMethodInvocation
Object someMethod(Object requestPayload) {
// ...
return responsePayload;
}
}
However it doesn't play, the logging aspect is not taken into the account.
There is spring's AnnotationUtils.findAnnotation which offers the needed functionality (of recognizing, whether the #LoggedMethodInvocation shall be considered). However, I don't know how to put this into the pointcut configuration.
How shall I modify the logging AOP config so it will recognize the logging annotation even if it is hidden behind the #MonitoredMethodInvocation?

Java Spring AOP: Can I ignore Xlint:invalidAbsoluteTypeName error which caused by pointcuts to classes which I don't use?

I have an issue concerning a generic component and one (of a dozen) application(s). My component has point cuts to many annotations, which could be used within classes and methods in my apps. When all annotations are present on the classpath, everything works fine. But not in all my apps I have these dependencies. The quick fix is, of course, add them, but that gives my app a lot of code which I don't need in that app. I'm searching for a way to ignore the Xlint:invalidAbsoluteTypeName error as stated here: Xlint:invalidAbsoluteTypeName
So what I have:
I have many apps with Soap/JMS connections, and all are annotated with the #Annotation org.springframework.ws.server.endpoint.annotation.Endpoint.
I have my pointcut in my generic component (jar):
#Around("within(#org.springframework.ws.server.endpoint.annotation.Endpoint *)")
And the result is:
All apps having the Spring WS dependency along with my generic component have no issues
Apps which don't have the annotation, cannot start due to java.lang.IllegalArgumentException: warning no match for this type name: org.springframework.ws.server.endpoint.annotation.Endpoint [Xlint:invalidAbsoluteTypeName] (which is obvious, see the link)
So the problem looks like Xlint:invalidAbsoluteTypeName BUT I don't want to add Spring dependencies which I'm not using. I just want this AOP pointcut ignored. Other workarounds like splitting up the pointcuts to different jars imho give too much overhead. Is there any way to have Spring AOP just ignore this pointcut, or e.g. set the pointcut to st like if-exists(class)?
To show why I think separating is causing way too much overhead have a look at my aspect structure:
#Aspect
public class PerformanceLoggingAspect {
private LogWriter logWriter;
#Inject
public PerformanceLoggingAspect(LogWriter logWriter) {
this.logWriter = logWriter;
}
#Around("within(#org.springframework.web.bind.annotation.RestController *)")
public Object withinARestController(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.REST);
}
#Around("within(#org.springframework.ws.server.endpoint.annotation.Endpoint *)")
public Object withinAnEndpoint(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.BERICHT);
}
#Around("within(#javax.inject.Named *)")
public Object withinAService(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.SERVICE);
}
private Object proceedWithLogging(ProceedingJoinPoint pjp, String metingType) throws Throwable {
(... Working code (performance logging) if the annotation is on the classpath...)
}
}
Update: I tried creating a #NeedsClass("any.package.Class") which is a #Conditional annotation from spring-context. The condition class is a ClasspathCondition which checked if the classloader could load that given class. But the error occurs before the condition gets evaluated so I'm afraid this is a dead end. But if you're curious:
The #NeedsClass annotation I tried
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
#Documented
#Conditional(ClasspathCondition.class)
public #interface NeedsClass {
String[] value();
}
The Condition implementation. I had logging here, which never got written
public class ClasspathCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
try {
String[] classes = (String[]) metadata.getAnnotationAttributes(NeedsClass.class.getName()).get("classes");
for (String clazz : classes) {
ClassUtils.resolveClassName(clazz, context.getClassLoader());
}
return true;
} catch (Throwable t) { /* noOp() */}
return false;
}
}
For now I have a workaround:
I created a superclass with the method:
protected Object proceedWithLogging(ProceedingJoinPoint pjp, String metingType) throws Throwable {
(... code which adds performance logging ...)
}
I created 4 subclasses with each the #Aspect annotation, and 1 method calling the super. For example this one targets JMS:
#Aspect
public class JmsPerformanceLogger extends PerformanceLoggingAspect {
#Inject
private LogWriter logWriter;
#Around("within(#org.springframework.ws.server.endpoint.annotation.Endpoint *)")
public Object withinAnEndpoint(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.BERICHT);
}
}
As a downside I have to configure all different beans which I need within my app, and I cannot add one simple configuration file as shown below, with all beans preconfigured:
#Configuration
public class PerformanceloggingConfig {
#Bean
public LogWriter performanceLogWriter(){
return new DefaultLogWriter();
}
#Bean
public JmsPerformanceLogger jmsPerformanceLogger(){
return new JmsPerformanceLogger();
}
#Bean
public RestPerformanceLogger restPerformanceLogger(){
return new RestPerformanceLogger();
}
#Bean
public ServicesPerformanceLogger servicesPerformanceLogger(){
return new ServicesPerformanceLogger();
}
#Bean
public DaoPerformanceLogger daoPerformanceLogger(){
return new DaoPerformanceLogger();
}
}
And therefore also not the handy annotation to autoconfig the class:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(PerformanceloggingConfig.class)
public #interface EnablePerformanceLogging {
}
But for now adding these 4 beans when I need them, makes it possible to differentiate per app. But of course this is still a workaround, as I want to use #EnablePerformanceLogging and be done with it. If anyone has a better answer, pls tell me

javaslang List.of() on cdi Instance

I have multiple class with a Qualifier that I created:
#ServiceComponent(restPath = "/trucks")
public class TruckService {
}
#ServiceComponent(restPath = "/cars")
public class CarService {
}
here is the Qualifier (not important for the question)
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({TYPE, FIELD})
public #interface ServiceComponent {
public boolean exposeAsRest() default true;
#Nonbinding public String restPath() default "";
#Nonbinding public String restGetPrefix() default "get,find,all";
#Nonbinding public String restPostPrefix() default "create,new,post";
}
in another class, I inject those instance using javax.enterprise.inject.Instance<>
class SomeConfigurationClasss {
#Inject
#ServiceComponent()
Instance<Object> _restComponents;
#Override
public void iterate() throws Exception {
//iterate
for(Object obj : _restComponents){
somefuncion(obj);
}
//List.of(_restComponents)
//.flatMap(obj -> somefuncion(obj));
}
}
if I execute the "normal" iteration (for...) I get the Object (TruckService or CarService) given as parameter to the somefunction().
but if I use javaslang's List.of(...) I get the Instance itself. Which I think it's the expected behavior
Is there a possibility to use List.of on a Instance that can contain one or multiple bean (depending on the injection binding). (I already try to call iterator(), select() on the Instance)
Instance<T> extends Iterable<T> so you should use List#ofAll(Iterable)

Using NotNull Annotation in method argument

I just started using the #NotNull annotation with Java 8 and getting some unexpected results.
I have a method like this:
public List<Found> findStuff(#NotNull List<Searching> searchingList) {
... code here ...
}
I wrote a JUnit test passing in the null value for the argument searchingList. I was expecting some type of error to happen but it went through as though the annotation was not there. Is this expected behavior? From what I understood, this was to allow you to skip writing the boilerplate null check code.
An explanation of what exactly #NotNull is supposed to do would be greatly appreciated.
#Nullable and #NotNull do nothing on their own. They are supposed to act as Documentation tools.
The #Nullable Annotation reminds you about the necessity to introduce an NPE check when:
Calling methods that can return null.
Dereferencing variables (fields, local variables, parameters) that can be null.
The #NotNull Annotation is, actually, an explicit contract declaring the following:
A method should not return null.
A variable (like fields, local variables, and parameters) cannot should not hold null value.
For example, instead of writing:
/**
* #param aX should not be null
*/
public void setX(final Object aX ) {
// some code
}
You can use:
public void setX(#NotNull final Object aX ) {
// some code
}
Additionally, #NotNull is often checked by ConstraintValidators (eg. in spring and hibernate).
The #NotNull annotation doesn't do any validation on its own because the annotation definition does not provide any ConstraintValidator type reference.
For more info see:
Bean validation
NotNull.java
Constraint.java
ConstraintValidator.java
As mentioned above #NotNull does nothing on its own. A good way of using #NotNull would be using it with Objects.requireNonNull
public class Foo {
private final Bar bar;
public Foo(#NotNull Bar bar) {
this.bar = Objects.requireNonNull(bar, "bar must not be null");
}
}
To make #NonNull active you need Lombok:
https://projectlombok.org/features/NonNull
import lombok.NonNull;
Follow: Which #NotNull Java annotation should I use?
If you are using Spring, you can force validation by annotating the class with #Validated:
import org.springframework.validation.annotation.Validated;
More info available here:
Javax #NotNull annotation usage
You could also use #NonNull from projectlombok (lombok.NonNull)
SO #NotNull just is a tag...If you want to validate it, then you must use something like hibernate validator jsr 303
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<List<Searching>> violations = validator.validate(searchingList);
I do this to create my own validation annotation and validator:
ValidCardType.java(annotation to put on methods/fields)
#Constraint(validatedBy = {CardTypeValidator.class})
#Documented
#Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface ValidCardType {
String message() default "Incorrect card type, should be among: \"MasterCard\" | \"Visa\"";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And, the validator to trigger the check:
CardTypeValidator.java:
public class CardTypeValidator implements ConstraintValidator<ValidCardType, String> {
private static final String[] ALL_CARD_TYPES = {"MasterCard", "Visa"};
#Override
public void initialize(ValidCardType status) {
}
public boolean isValid(String value, ConstraintValidatorContext context) {
return (Arrays.asList(ALL_CARD_TYPES).contains(value));
}
}
You can do something very similar to check #NotNull.
To test your method validation in a test, you have to wrap it a proxy in the #Before method.
#Before
public void setUp() {
this.classAutowiredWithFindStuffMethod = MethodValidationProxyFactory.createProxy(this.classAutowiredWithFindStuffMethod);
}
With MethodValidationProxyFactory as :
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
public class MethodValidationProxyFactory {
private static final StaticApplicationContext ctx = new StaticApplicationContext();
static {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.afterPropertiesSet(); // init advisor
ctx.getBeanFactory()
.addBeanPostProcessor(processor);
}
#SuppressWarnings("unchecked")
public static <T> T createProxy(T instance) {
return (T) ctx.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(instance, instance.getClass()
.getName());
}
}
And then, add your test :
#Test
public void findingNullStuff() {
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> this.classAutowiredWithFindStuffMethod.findStuff(null));
}
I resolved it with
#JsonSetter(nulls = Nulls.AS_EMPTY)
#NotBlank
public String myString;
Request Json:
{
myString=null
}
Response:
error must not be blank

Invoke other method instead

I have two methods and one of them with an annotation, let's say:
#ReplacingMethod(bar)
public void foo() { ... }
public void bar { ... }
Is it possible to invoke bar instead of foo whenever foo is called, without jumping into the body of foo? I did some research on this and were not able to set a return value via reflections. Any suggestions?
You can achieve this using Aspect Oriented Programming, e.g. with Spring AOP. I don't think you can change method implementation in pure Java without AOP.
Let me give you an example how to achieve what you asked for with Spring AOP. First, define your annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReplacingMethod {
String value();
}
Then define an aspect that will do the actual replacing of method:
#Aspect // aspect is a module encapsulating your replacing functionality
public class ReplacingAspect {
// pointcut gives an expression selecting the "joint points" to be intercepted
#Pointcut("#annotation(example.annotation.ReplacingMethod)")
public void methodToBeReplaced() { }
// advice defining the code executed at joint points selected by given pointcut;
// in our case #Around is executed instead of the method call selected by pointcut methodToBeReplaced()
#Around("methodToBeReplaced()")
public void replaceMethodCall(ProceedingJoinPoint pjp) throws Throwable {
// get reference to the method to be replaced
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
// extract the name of the method to be called from ReplacingMethod annotation
ReplacingMethod replacingMethodAnnotation = method.getAnnotation(ReplacingMethod.class);
String methodToCallName = replacingMethodAnnotation.value();
// use reflection to call the method
Method methodToCall = pjp.getTarget().getClass().getMethod(methodToCallName);
methodToCall.invoke(pjp.getTarget());
}
}
Now, assuming you have class TestClass where you have applied your #ReplacingMethod annotation,
public class TestClass {
#ReplacingMethod("bar")
public void foo() { System.out.println("foo"); }
public void bar() { System.out.println("bar"); }
}
the last missing piece is to get create your instance of TestClass with AOP enabled and your ReplacingAspect applied:
public class Main {
public static void main(String... args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); // create Spring context that enables AOP under the hood
TestClass testObject = context.getBean(TestClass.class); // we get reference to TestClass instance from context; calling on a plain new instance wouldn't work
testObject.foo(); // prints "bar" !
}
#EnableAspectJAutoProxy // enables AOP support
#Configuration
public static class TestConfiguration {
#Bean public TestClass testClass() { return new TestClass(); }
#Bean public ReplacingAspect aspect() { return new ReplacingAspect(); } // enables our ReplacingAspect
}
}
You can check out the whole working example at GitHub.
Reflection cannot change the schema of a class and not its behaviour. It can only call (possibly hidden) features.
If you want to replace a method call by another try out a byte code library as asm or javassist. These tools allow you to change class definitions and behaviour (even at runtime with some restrictions).
The approach with AOP is easier, but it is not as flexible and its classpath footprint is larger.

Categories