Skip to content

Commit

Permalink
Provide a new Java Rule checking serialVersionUID is static final long
Browse files Browse the repository at this point in the history
CLoses gh-44
  • Loading branch information
mnhock authored and mnhock committed Jun 19, 2024
1 parent eb44c8f commit 22b1ef7
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 2 deletions.
13 changes: 13 additions & 0 deletions docs/USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Architecture rules are defined using Taikai's fluent API, allowing developers to
| | General | `noUsageOfSystemOutOrErr` | Disallow usage of `System.out` or `System.err` | Default (WITHOUT_TESTS) |
| | General | `utilityClassesShouldBeFinalAndHavePrivateConstructor` | Utility classes should be `final` and have a private constructor | Default (WITHOUT_TESTS) |
| | General | `finalClassesShouldNotHaveProtectedMembers` | Ensures that classes declared as `final` do not contain any `protected` members | Default (WITHOUT_TESTS) |
| | General | `serialVersionUIDShouldBeStaticFinalLong` | Ensure that fields named `serialVersionUID` are declared as `static final long` | Default (WITHOUT_TESTS) |
| | Imports | `shouldHaveNoCycles` | No cyclic dependencies in imports | Default (WITHOUT_TESTS) |
| | Imports | `shouldNotImport` | Disallow specific imports (e.g., `..shaded..`) | Default (WITHOUT_TESTS) |
| | Naming | `classesShouldNotMatch` | Classes should not match specific naming patterns (e.g., `.*Impl`) | Default (WITHOUT_TESTS) |
Expand Down Expand Up @@ -276,6 +277,18 @@ Taikai.builder()
.check();
```

- **Ensure `serialVersionUID` is `static final long`**: Ensure that fields named `serialVersionUID` are declared as `static final long`.

```java
Taikai.builder()
.namespace("com.company.yourproject")
.test(test -> test
.junit5(junit5 -> junit5
.serialVersionUIDShouldBeStaticFinalLong())
.build()
.check();
```

### Spring Configuration

Spring configuration involves defining constraints specific to Spring Framework usage.
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/enofex/taikai/java/ConstantNaming.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ static ArchCondition<JavaField> shouldFollowConstantNamingConvention() {

@Override
public void check(JavaField field, ConditionEvents events) {
if (!field.getOwner().isEnum() && !CONSTANT_NAME_PATTERN.matcher(field.getName())
.matches()) {
if (!field.getOwner().isEnum()
&& !"serialVersionUID".equals(field.getName())
&& !CONSTANT_NAME_PATTERN.matcher(field.getName()).matches()) {
String message = String.format(
"Constant %s in class %s does not follow the naming convention", field.getName(),
field.getOwner().getName());
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/enofex/taikai/java/JavaConfigurer.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import static com.enofex.taikai.java.HashCodeAndEquals.implementHashCodeAndEquals;
import static com.enofex.taikai.java.NoSystemOutOrErr.notUseSystemOutOrErr;
import static com.enofex.taikai.java.ProtectedMembers.notHaveProtectedMembers;
import static com.enofex.taikai.java.SerialVersionUID.beStaticFinalLong;
import static com.enofex.taikai.java.SerialVersionUID.namedSerialVersionUID;
import static com.enofex.taikai.java.UtilityClasses.havePrivateConstructor;
import static com.enofex.taikai.java.UtilityClasses.utilityClasses;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
Expand Down Expand Up @@ -117,6 +119,17 @@ public JavaConfigurer finalClassesShouldNotHaveProtectedMembers(Configuration co
.as("Final classes should not have protected members"), configuration));
}

public JavaConfigurer serialVersionUIDFieldsShouldBeStaticFinalLong() {
return serialVersionUIDFieldsShouldBeStaticFinalLong(null);
}

public JavaConfigurer serialVersionUIDFieldsShouldBeStaticFinalLong(Configuration configuration) {
return addRule(TaikaiRule.of(fields()
.that(namedSerialVersionUID())
.should(beStaticFinalLong())
.as("serialVersionUID should be static final long"), configuration));
}

@Override
public void disable() {
disable(ImportsConfigurer.class);
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/com/enofex/taikai/java/SerialVersionUID.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.enofex.taikai.java;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaField;
import com.tngtech.archunit.core.domain.JavaModifier;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ConditionEvents;
import com.tngtech.archunit.lang.SimpleConditionEvent;

final class SerialVersionUID {

private SerialVersionUID() {
}

static ArchCondition<JavaField> beStaticFinalLong() {
return new ArchCondition<>("be static final long") {
@Override
public void check(JavaField javaField, ConditionEvents events) {
boolean isStatic = javaField.getModifiers().contains(JavaModifier.STATIC);
boolean isFinal = javaField.getModifiers().contains(JavaModifier.FINAL);
boolean isLong = javaField.getRawType().isEquivalentTo(long.class);

if (!isStatic || !isFinal || !isLong) {
String message = String.format("Field %s in class %s is not static final long",
javaField.getName(), javaField.getOwner().getName());
events.add(SimpleConditionEvent.violated(javaField, message));
}
}
};
}

static DescribedPredicate<JavaField> namedSerialVersionUID() {
return new DescribedPredicate<>("named serialVersionUID") {

@Override
public boolean test(JavaField javaField) {
return "serialVersionUID".equals(javaField.getName());
}
};
}
}
1 change: 1 addition & 0 deletions src/test/java/com/enofex/taikai/ArchitectureTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ void shouldFulfilConstrains() {
.utilityClassesShouldBeFinalAndHavePrivateConstructor()
.methodsShouldNotDeclareGenericExceptions()
.fieldsShouldNotBePublic()
.serialVersionUIDFieldsShouldBeStaticFinalLong()
.imports(imports -> imports
.shouldHaveNoCycles()
.shouldNotImport("..shaded..")
Expand Down
1 change: 1 addition & 0 deletions src/test/java/com/enofex/taikai/Usage.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static void main(String[] args) {
.methodsShouldNotDeclareGenericExceptions()
.finalClassesShouldNotHaveProtectedMembers()
.utilityClassesShouldBeFinalAndHavePrivateConstructor()
.serialVersionUIDFieldsShouldBeStaticFinalLong()
.imports(imports -> imports
.shouldHaveNoCycles()
.shouldNotImport("..shaded..")
Expand Down

0 comments on commit 22b1ef7

Please sign in to comment.