Skip to content

Commit

Permalink
feat: Support @MethodBan in JavaX EE
Browse files Browse the repository at this point in the history
  • Loading branch information
PENEKhun committed Jan 29, 2024
1 parent 375bc94 commit b8c105a
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,15 @@ public enum FullyQualifiedClassName {
/**
* Get the name of the Fully Qualified Class Name.
*
* @param isJavax true if the target FQCN is JavaX EE, false if the target Jakarta EE. (more info: <a
* href="https://jakarta.ee/blogs/javax-jakartaee-namespace-ecosystem-progress/">article</a>)
* @return the name of the FQCN string
*/
public String getName() {
return name;
public String getName(boolean isJavax) {
if (isJavax) {
return this.name.replace("jakarta", "javax");
}
return this.name;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

/**
* MethodBan Annotation.
*
* @author PENEKhun
*/
@Target(ElementType.METHOD)
Expand All @@ -33,16 +34,24 @@
* The number of times the method can be called within the time period.
*/
int times() default 5;

/**
* The time period in seconds.
*/
int seconds() default 60;

/**
* The number of seconds to ban the user from calling the method.
*/
int banSeconds() default 60;

/**
* The message to pass when the user is banned.
*/
String banMessage() default "You have been banned from calling this method. Please try again later.";

/**
* If you are using a version prior to the namespace change to Jakarta, please set this option to true.
*/
boolean javax() default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public Set<String> getSupportedAnnotationTypes() {

@Override
public SourceVersion getSupportedSourceVersion() {
if (processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) < 0) {
processingEnv.getMessager().printMessage(ERROR, "MethodBan is only supported in Java 8 or higher.");
}

return SourceVersion.latestSupported();
}

Expand All @@ -74,20 +78,22 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
if (isAlreadyProcessed) {
break;
}

final boolean isJavaxNamespace = element.getAnnotation(MethodBan.class).javax();
checkValidMethodBan(element);
processMethodBan(element);
processMethodBan(element, isJavaxNamespace);
isAlreadyProcessed = true;
}
return true;
}

private void processMethodBan(Element element) {
private void processMethodBan(Element element, boolean isJavaxNamespace) {
generateEnableAopClass(element);
generateMethodBanAspect(element);
generateMethodBanAspect(element, isJavaxNamespace);
}

private void generateMethodBanAspect(Element element) {
final ClassName before = ClassName.bestGuess(BEFORE.getName());
private void generateMethodBanAspect(Element element, boolean isJavaxNamespace) {
final ClassName before = ClassName.bestGuess(BEFORE.getName(isJavaxNamespace));
final AnnotationSpec annotationSpec = AnnotationSpec.builder(before)
.addMember("value", "$S", "@annotation(%s)" .formatted(ClassName.get(MethodBan.class)))
.build();
Expand All @@ -97,9 +103,9 @@ private void generateMethodBanAspect(Element element) {
.returns(String.class)
.addCode(CodeBlock.builder()
.addStatement("$T request = (($T) $T.getRequestAttributes()).getRequest()",
ClassName.bestGuess(HTTP_SERVLET_REQUEST.getName()),
ClassName.bestGuess(SERVLET_REQUEST_ATTRIBUTES.getName()),
ClassName.bestGuess(REQUEST_CONTEXT_HOLDER.getName()))
ClassName.bestGuess(HTTP_SERVLET_REQUEST.getName(isJavaxNamespace)),
ClassName.bestGuess(SERVLET_REQUEST_ATTRIBUTES.getName(isJavaxNamespace)),
ClassName.bestGuess(REQUEST_CONTEXT_HOLDER.getName(isJavaxNamespace)))
.addStatement("$T xForwardedForHeader = $L.getHeader($S)", String.class, "request",
"X-Forwarded-For")
.beginControlFlow("if ($L == null)", "xForwardedForHeader")
Expand All @@ -122,14 +128,14 @@ private void generateMethodBanAspect(Element element) {
final MethodSpec methodSpec = MethodSpec.methodBuilder("beforeMethodBan" + System.nanoTime())
.addModifiers(Modifier.PUBLIC)
.addAnnotation(annotationSpec)
.addParameter(ClassName.bestGuess(JOIN_POINT.getName()), "joinPoint")
.addParameter(ClassName.bestGuess(JOIN_POINT.getName(isJavaxNamespace)), "joinPoint")
.addCode(codes)
.build();

final TypeSpec classSpec = TypeSpec.classBuilder("MethodBanAspect" + System.nanoTime())
.addModifiers(Modifier.PUBLIC)
.addAnnotation(ClassName.bestGuess(ASPECT.getName()))
.addAnnotation(ClassName.bestGuess(COMPONENT.getName()))
.addAnnotation(ClassName.bestGuess(ASPECT.getName(isJavaxNamespace)))
.addAnnotation(ClassName.bestGuess(COMPONENT.getName(isJavaxNamespace)))
.addMethod(methodSpec)
.addMethod(getUserIpMethodSpec)
.build();
Expand All @@ -142,8 +148,8 @@ private void generateMethodBanAspect(Element element) {
private void generateEnableAopClass(Element element) {
final TypeSpec enableAopClass = TypeSpec.classBuilder("EnableAopClass" + System.nanoTime())
.addModifiers(Modifier.PUBLIC)
.addAnnotation(ClassName.bestGuess(COMPONENT.getName()))
.addAnnotation(ClassName.bestGuess(ENABLE_ASPECT_JAUTO_PROXY.getName()))
.addAnnotation(ClassName.bestGuess(COMPONENT.getName(false)))
.addAnnotation(ClassName.bestGuess(ENABLE_ASPECT_JAUTO_PROXY.getName(false)))
.build();

final String packageName = element.getEnclosingElement().toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,28 @@ class FullyQualifiedClassNameTest {
"org.springframework.web.context.request.ServletRequestAttributes, SERVLET_REQUEST_ATTRIBUTES",
"org.springframework.web.context.request.RequestContextHolder, REQUEST_CONTEXT_HOLDER"
})
void getNameReturnWell(String expected, FullyQualifiedClassName fqcn) {
assertEquals(expected, fqcn.getName());
void getNameReturnWellWhenJakarta(String expected, FullyQualifiedClassName fqcn) {
assertEquals(expected, fqcn.getName(false));
}

@ParameterizedTest
@CsvSource({
"org.springframework.web.bind.annotation.GetMapping, GET_MAPPING",
"org.springframework.web.bind.annotation.PostMapping, POST_MAPPING",
"org.springframework.web.bind.annotation.PutMapping, PUT_MAPPING",
"org.springframework.web.bind.annotation.DeleteMapping, DELETE_MAPPING",
"org.springframework.web.bind.annotation.PatchMapping, PATCH_MAPPING",
"org.springframework.stereotype.Component, COMPONENT",
"org.springframework.context.annotation.EnableAspectJAutoProxy, ENABLE_ASPECT_JAUTO_PROXY",
"org.aspectj.lang.JoinPoint, JOIN_POINT",
"org.aspectj.lang.annotation.Aspect, ASPECT",
"org.aspectj.lang.annotation.Before, BEFORE",
"javax.servlet.http.HttpServletRequest, HTTP_SERVLET_REQUEST",
"org.springframework.web.context.request.ServletRequestAttributes, SERVLET_REQUEST_ATTRIBUTES",
"org.springframework.web.context.request.RequestContextHolder, REQUEST_CONTEXT_HOLDER"
})
void getNameReturnWellWhenJavax(String expected, FullyQualifiedClassName fqcn) {
assertEquals(expected, fqcn.getName(true));
}

@ParameterizedTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@
@SuppressWarnings("checkstyle:FinalLocalVariable")
class MethodBanProcessorTest {

@Test
void methodBanNotSupportUnderJava8() {
// given
JavaFileObject src = JavaFileObjects.forResource("before/MethodBanHappy.java");
String javaVersion = "7";

// when
Compilation compilation = javac()
.withProcessors(new MethodBanProcessor())
.withOptions("-source", javaVersion, "-target", javaVersion)
.compile(src);

// then
assertThat(compilation).hadErrorContaining("MethodBan is only supported in Java 8 or higher.");
}

@Test
void compileHappy() {
JavaFileObject src = JavaFileObjects.forResource("before/MethodBanHappy.java");
Expand Down

0 comments on commit b8c105a

Please sign in to comment.