Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support @MethodBan in JavaX EE #5

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config/checkstyle/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@
<suppress id="PreferReentrantShortLock" files="[\\/](examples|it|jmh|test)[\\/]" />
<!-- Suppress checks related with integration tests. -->
<suppress files="[\\/]resources\/before[\\/]" checks="[a-zA-Z0-9]*"/>
<!-- Suppress final Keyword related checks in non-main code. -->
<suppress id="FinalLocalVariable" files="[\\/](examples|test)[\\/]" />
</suppressions>
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
Loading