Skip to content

Instantly share code, notes, and snippets.

@kriegaex
Created August 16, 2021 02:11
Show Gist options
  • Save kriegaex/7fb3ff5313fe089a503f24363849557e to your computer and use it in GitHub Desktop.
Save kriegaex/7fb3ff5313fe089a503f24363849557e to your computer and use it in GitHub Desktop.
package de.scrum_master.spring.q68785567;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
@SpringBootApplication
@Configuration
public class DemoApplication {
private static final Logger log = LoggerFactory.getLogger(DemoApplication.class.getName());
public static void main(String[] args) throws InterruptedException, IOException, ExecutionException {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) {
TestDummyController controller = appContext.getBean(TestDummyController.class);
TestDummyValueHolder.setValue("value: non-null");
TestDummyValueHolder.setAnotherValue("another value: non-null");
controller.direct();
controller.directRepeat();
controller.meta();
controller.meta2();
controller.mixed();
controller.mixedRepeat();
log.info("----------------------------------------------------------------------");
TestDummyValueHolder.setValue(null);
TestDummyValueHolder.setAnotherValue(null);
try { controller.direct(); } catch (Exception e) { log.info("Caught exception: " + e); }
try { controller.directRepeat(); } catch (Exception e) { log.info("Caught exception: " + e); }
try { controller.meta(); } catch (Exception e) { log.info("Caught exception: " + e); }
try { controller.meta2(); } catch (Exception e) { log.info("Caught exception: " + e); }
try { controller.mixed(); } catch (Exception e) { log.info("Caught exception: " + e); }
try { controller.mixedRepeat(); } catch (Exception e) { log.info("Caught exception: " + e); }
}
}
package de.scrum_master.spring.q68785567;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Repeatable(RequiresNonBlanks.class)
@Documented
public @interface RequiresNonBlank {
String value();
Class<? extends StuffException> throwIfInvalid();
}
package de.scrum_master.spring.q68785567;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Component
@Aspect
//@Slf4j
public class RequiresNonBlankAspect {
private static final Logger log = LoggerFactory.getLogger(RequiresNonBlankAspect.class.getName());
private static final String REQUIRES_NON_BLANK_FQPN =
"de.scrum_master.spring.q68785567.RequiresNonBlank";
private ExpressionParser elParser = new SpelExpressionParser();
@Before("execution(@" + REQUIRES_NON_BLANK_FQPN + " * *(..)) && @annotation(requiresNonBlank)")
public void directAnnotation(JoinPoint joinPoint, RequiresNonBlank requiresNonBlank) {
log.info("[DIRECT] " + joinPoint + " -> " + requiresNonBlank);
evaluatePrecondition(joinPoint, requiresNonBlank);
}
@Before("execution(@" + REQUIRES_NON_BLANK_FQPN + "s * *(..)) && @annotation(requiresNonBlanks)")
public void directAnnotationRepeated(JoinPoint joinPoint, RequiresNonBlanks requiresNonBlanks) {
for (RequiresNonBlank requiresNonBlank : requiresNonBlanks.value()) {
log.info("[DIRECT-REPEAT] " + joinPoint + " -> " + requiresNonBlank);
evaluatePrecondition(joinPoint, requiresNonBlank);
}
}
@Before("execution(@(@" + REQUIRES_NON_BLANK_FQPN + " *) * *(..))")
public void metaAnnotations(JoinPoint joinPoint) {
for (RequiresNonBlank requiresNonBlank : getMetaAnnotations(joinPoint)) {
log.info("[META] " + joinPoint + " -> " + requiresNonBlank);
evaluatePrecondition(joinPoint, requiresNonBlank);
}
}
private Set<RequiresNonBlank> getMetaAnnotations(JoinPoint joinPoint) {
Set<RequiresNonBlank> foundMetaAnnotations = new HashSet<>();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
foundMetaAnnotations.addAll(
Arrays.asList(
annotation.annotationType().getAnnotationsByType(RequiresNonBlank.class)
)
);
}
return foundMetaAnnotations;
}
public void evaluatePrecondition(JoinPoint joinPoint, RequiresNonBlank requiresNonBlank) {
try {
Objects.requireNonNull(requiresNonBlank);
}
catch (NullPointerException e) {
log.error("No annotation found!", e);
}
Expression expression = elParser.parseExpression(requiresNonBlank.value());
String expressionToEvaluate = (String) expression.getValue(joinPoint.getArgs());
log.info("Evaluated expression: " + expressionToEvaluate);
if (StringUtils.isEmpty(expressionToEvaluate)) {
try {
throw requiresNonBlank.throwIfInvalid().getConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
log.error("Could not throw the exception configured!", e);
}
}
}
}
package de.scrum_master.spring.q68785567;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD })
@Documented
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).value",
throwIfInvalid = TestDummyStuffException.class
)
public @interface RequiresNonBlankMeta {}
package de.scrum_master.spring.q68785567;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD })
@Documented
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).anotherValue",
throwIfInvalid = TestDummyStuffException.class
)
public @interface RequiresNonBlankMeta2 {}
package de.scrum_master.spring.q68785567;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
public @interface RequiresNonBlanks {
RequiresNonBlank[] value();
}
package de.scrum_master.spring.q68785567;
public class StuffException extends RuntimeException {
public StuffException() {
super();
}
public StuffException(String message) {
super(message);
}
public StuffException(String message, Throwable cause) {
super(message, cause);
}
}
package de.scrum_master.spring.q68785567;
import org.springframework.stereotype.Component;
@Component
public class TestDummyController {
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).value",
throwIfInvalid = TestDummyStuffException.class
)
public boolean direct() {
return true;
}
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).value",
throwIfInvalid = TestDummyStuffException.class
)
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).anotherValue",
throwIfInvalid = TestDummyStuffException.class
)
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).anotherValue",
throwIfInvalid = TestDummyStuffException.class
)
public boolean directRepeat() {
return true;
}
@RequiresNonBlankMeta
public boolean meta() {
return true;
}
@RequiresNonBlankMeta2
public boolean meta2() {
return true;
}
@RequiresNonBlankMeta
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).anotherValue",
throwIfInvalid = TestDummyStuffException.class
)
public boolean mixed() {
return true;
}
@RequiresNonBlankMeta
@RequiresNonBlankMeta2
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).value",
throwIfInvalid = TestDummyStuffException.class
)
@RequiresNonBlank(
value = "T(de.scrum_master.spring.q68785567.TestDummyValueHolder).anotherValue",
throwIfInvalid = TestDummyStuffException.class
)
public boolean mixedRepeat() {
return true;
}
}
package de.scrum_master.spring.q68785567;
public class TestDummyStuffException extends StuffException {
public TestDummyStuffException() {
super();
}
public TestDummyStuffException(String message) {
super(message);
}
public TestDummyStuffException(String message, Throwable cause) {
super(message, cause);
}
}
package de.scrum_master.spring.q68785567;
public class TestDummyValueHolder {
private static String value;
private static String anotherValue;
public static String getValue() {
return value;
}
public static void setValue(String newValue) {
value = newValue;
}
public static String getAnotherValue() {
return anotherValue;
}
public static void setAnotherValue(String newAnotherValue) {
anotherValue = newAnotherValue;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment