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

Add javax.Generated annotation to classes generated by the java-generator #4443

Merged
merged 2 commits into from
Sep 30, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#### New Features
* Fix #4398: add annotation @PreserveUnknownFields for marking generated field have `x-kubernetes-preserve-unknown-fields: true` defined
* Fix #4351: add `javax.annotation.processing.Generated` to classes generated with the `java-generator`

#### _**Note**_: Breaking changes in the API
* Fix #4350: SchemaSwap's fieldName parameter now expects a field name only, not a method or a constructor.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,25 @@
import io.sundr.model.TypeDef;
import io.sundr.model.repo.DefinitionRepository;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;

import javax.annotation.processing.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;

@SupportedAnnotationTypes({"io.fabric8.kubernetes.model.annotation.Version"})
@SupportedAnnotationTypes({ "io.fabric8.kubernetes.model.annotation.Version" })
public class CustomResourceAnnotationProcessor extends AbstractProcessor {

private final CRDGenerator generator = new CRDGenerator();

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Expand All @@ -54,7 +56,8 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
final CRDGenerationInfo allCRDs = generator.withOutput(new FileObjectCRDOutput(processingEnv)).detailedGenerate();
allCRDs.getCRDDetailsPerNameAndVersion().forEach((crdName, versionToInfo) -> {
messager.printMessage(Diagnostic.Kind.NOTE, "Generating CRD " + crdName + ":\n");
versionToInfo.forEach((version, info) -> messager.printMessage(Diagnostic.Kind.NOTE, " - " + version + " -> " + info.getFilePath()));
versionToInfo.forEach(
(version, info) -> messager.printMessage(Diagnostic.Kind.NOTE, " - " + version + " -> " + info.getFilePath()));
});
return true;
}
Expand All @@ -66,18 +69,27 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
for (TypeElement annotation : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
if (element instanceof TypeElement) {
try {
// The annotation is loaded with reflection for compatibility with Java 8
Class<Annotation> generatedAnnotation = (Class<Annotation>) Class.forName("javax.annotation.processing.Generated");
if (element.getAnnotationsByType(generatedAnnotation).length > 0) {
continue;
}
} catch (ClassNotFoundException e) {
// ignore
}
generator.customResources(toCustomResourceInfo((TypeElement) element));
}
}
}

return false;
}

private CustomResourceInfo toCustomResourceInfo(TypeElement customResource) {
TypeDef definition = Adapters.adaptType(customResource, AptContext.getContext());
definition = Types.unshallow(definition);

if (CustomResourceInfo.DESCRIBE_TYPE_DEFS) {
Types.output(definition);
}
Expand All @@ -86,37 +98,38 @@ private CustomResourceInfo toCustomResourceInfo(TypeElement customResource) {
SpecAndStatus specAndStatus = Types.resolveSpecAndStatusTypes(definition);
if (specAndStatus.isUnreliable()) {
System.out.println("Cannot reliably determine status types for " + crClassName
+ " because it isn't parameterized with only spec and status types. Status replicas detection will be deactivated.");
+ " because it isn't parameterized with only spec and status types. Status replicas detection will be deactivated.");
}

final String group = customResource.getAnnotation(Group.class).value();
final String version = customResource.getAnnotation(Version.class).value();

final String kind = Optional.ofNullable(customResource.getAnnotation(Kind.class))
.map(Kind::value)
.orElse(customResource.getSimpleName().toString());
.map(Kind::value)
.orElse(customResource.getSimpleName().toString());

final String singular = Optional.ofNullable(customResource.getAnnotation(Singular.class))
.map(Singular::value)
.orElse(kind.toLowerCase(Locale.ROOT));
.map(Singular::value)
.orElse(kind.toLowerCase(Locale.ROOT));

final String plural = Optional.ofNullable(customResource.getAnnotation(Plural.class))
.map(Plural::value)
.map(s -> s.toLowerCase(Locale.ROOT))
.orElse(Pluralize.toPlural(singular));
.map(Plural::value)
.map(s -> s.toLowerCase(Locale.ROOT))
.orElse(Pluralize.toPlural(singular));

final String[] shortNames = Optional
.ofNullable(customResource.getAnnotation(ShortNames.class))
.map(ShortNames::value)
.orElse(new String[]{});
.ofNullable(customResource.getAnnotation(ShortNames.class))
.map(ShortNames::value)
.orElse(new String[] {});

final boolean storage = customResource.getAnnotation(Version.class).storage();
final boolean served = customResource.getAnnotation(Version.class).served();

final Scope scope = Types.isNamespaced(definition) ? Scope.NAMESPACED : Scope.CLUSTER;

return new CustomResourceInfo(group, version, kind, singular, plural, shortNames, storage, served, scope, definition, crClassName.toString(),
specAndStatus.getSpecClassName(), specAndStatus.getStatusClassName());
return new CustomResourceInfo(group, version, kind, singular, plural, shortNames, storage, served, scope, definition,
crClassName.toString(),
specAndStatus.getSpecClassName(), specAndStatus.getStatusClassName());
}

private static class FileObjectOutputStream extends OutputStream {
Expand Down Expand Up @@ -170,8 +183,8 @@ public FileObjectCRDOutput(ProcessingEnvironment processingEnv) {
@Override
protected FileObjectOutputStream createStreamFor(String crdName) throws IOException {
return new FileObjectOutputStream(
processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "",
"META-INF/fabric8/" + crdName + ".yml"));
processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "",
"META-INF/fabric8/" + crdName + ".yml"));
}

@Override
Expand All @@ -180,4 +193,3 @@ public URI crdURI(String crdName) {
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public class GenerateJavaSources implements Runnable {
"--code-structure" }, description = "Generate classes using a specific layout", required = false, hidden = true)
String codeStructure = null;

@Option(names = { "-skip-generated-annotations",
"--skip-generated-annotations" }, description = "Add extra lombok and sundrio annotation to the generated classes", required = false, hidden = true)
Boolean skipGeneratedAnnotations = null;

@Override
public void run() {
final Config.Prefix pSt = (prefixStrategy != null) ? Config.Prefix.valueOf(prefixStrategy) : null;
Expand All @@ -66,7 +70,8 @@ public void run() {
sSt,
alwaysPreserveUnkownFields,
addExtraAnnotations,
structure);
structure,
!skipGeneratedAnnotations);
final CRGeneratorRunner runner = new CRGeneratorRunner(config);
runner.run(source, target);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ public enum Suffix {
private static final boolean DEFAULT_ALWAYS_PRESERVE_FIELDS = false;
private static final boolean DEFAULT_ADD_EXTRA_ANNOTATIONS = false;
private static final CodeStructure DEFAULT_CODE_STRUCTURE = CodeStructure.PACKAGE_NESTED;
private static final boolean DEFAULT_ADD_GENERATED_ANNOTATIONS = true;

private Boolean uppercaseEnums = DEFAULT_UPPERCASE_ENUM;
private Prefix prefixStrategy = DEFAULT_PREFIX_STRATEGY;
private Suffix suffixStrategy = DEFAULT_SUFFIX_STRATEGY;
private Boolean alwaysPreserveUnknownFields = DEFAULT_ALWAYS_PRESERVE_FIELDS;
private Boolean objectExtraAnnotations = DEFAULT_ADD_EXTRA_ANNOTATIONS;
private CodeStructure structure = DEFAULT_CODE_STRUCTURE;
private Boolean generatedAnnotations = DEFAULT_ADD_GENERATED_ANNOTATIONS;

public Config() {
}
Expand All @@ -56,7 +58,8 @@ public Config(
Suffix suffixStrategy,
Boolean alwaysPreserveUnknownFields,
Boolean objectExtraAnnotations,
CodeStructure structure) {
CodeStructure structure,
Boolean generatedAnnotations) {
if (uppercaseEnums != null) {
this.uppercaseEnums = uppercaseEnums;
}
Expand All @@ -75,6 +78,9 @@ public Config(
if (structure != null) {
this.structure = structure;
}
if (generatedAnnotations != null) {
this.generatedAnnotations = generatedAnnotations;
}
}

public boolean isUppercaseEnums() {
Expand Down Expand Up @@ -104,4 +110,10 @@ public boolean isObjectExtraAnnotations() {
public CodeStructure getCodeStructure() {
return (structure == null) ? DEFAULT_CODE_STRUCTURE : structure;
}

public boolean isGeneratedAnnotations() {
return (generatedAnnotations == null)
? DEFAULT_ADD_GENERATED_ANNOTATIONS
: generatedAnnotations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
package io.fabric8.java.generator.nodes;

import com.fasterxml.jackson.databind.JsonNode;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.Name;
import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import io.fabric8.java.generator.Config;
import io.fabric8.java.generator.exceptions.JavaGeneratorException;
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
Expand All @@ -39,6 +43,10 @@ public abstract class AbstractJSONSchema2Pojo {
static final String OBJECT_CRD_TYPE = "object";
static final String ARRAY_CRD_TYPE = "array";

public static final AnnotationExpr GENERATED_ANNOTATION = new SingleMemberAnnotationExpr(
new Name("javax.annotation.processing.Generated"),
new StringLiteralExpr("io.fabric8.java.generator.CRGeneratorRunner"));

protected final String description;
protected final Config config;
protected final boolean isNullable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ public GeneratorResult generateJava() {
clz.addExtendedType(crType);
clz.addImplementedType("io.fabric8.kubernetes.api.model.Namespaced");

if (config.isGeneratedAnnotations()) {
clz.addAnnotation(GENERATED_ANNOTATION);
}
if (config.isObjectExtraAnnotations()) {
addExtraAnnotations(clz);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ public GeneratorResult generateJava() {
new NameExpr(
"using = com.fasterxml.jackson.databind.JsonDeserializer.None.class")));

if (config.isGeneratedAnnotations()) {
clz.addAnnotation(GENERATED_ANNOTATION);
}
if (config.isObjectExtraAnnotations()) {
addExtraAnnotations(clz);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private static Stream<Arguments> getCRDGenerationInputData() {
return Stream.of(
Arguments.of("testCrontabCrd", "crontab-crd.yml", "CronTab", "CrontabJavaCr", new Config()),
Arguments.of("testCrontabExtraAnnotationsCrd", "crontab-crd.yml", "CronTab", "CrontabJavaExtraAnnotationsCr",
new Config(null, null, null, null, Boolean.TRUE, null)),
new Config(null, null, null, null, Boolean.TRUE, null, true)),
Arguments.of("testKeycloakCrd", "keycloak-crd.yml", "Keycloak", "KeycloakJavaCr", new Config()),
Arguments.of("testJokeCrd", "jokerequests-crd.yml", "JokeRequest", "JokeRequestJavaCr", new Config()),
Arguments.of("testAkkaMicroservicesCrd", "akka-microservices-crd.yml", "AkkaMicroservice", "AkkaMicroserviceJavaCr",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class CompilationTest {

private static TemporaryFolder tmpFolder = TemporaryFolder.builder().build();

CRGeneratorRunner defaultRunner = new CRGeneratorRunner(new Config());
CRGeneratorRunner defaultRunner = new CRGeneratorRunner(
new Config(null, null, null, null, null, Config.CodeStructure.PACKAGE_NESTED, false));

List<JavaFileObject> getSources(File basePath) throws IOException {
List<JavaFileObject> sources = new ArrayList<JavaFileObject>();
Expand Down Expand Up @@ -87,7 +88,7 @@ void testCrontabCRDCompilesWithFlatPackage() throws Exception {
File crd = getCRD("crontab-crd.yml");
File dest = tmpFolder.newFolder("crontab-flat");
CRGeneratorRunner runner = new CRGeneratorRunner(
new Config(null, null, null, null, null, Config.CodeStructure.FLAT));
new Config(null, null, null, null, null, Config.CodeStructure.FLAT, false));

// Act
runner.run(crd, dest);
Expand All @@ -104,7 +105,7 @@ void testCrontabCRDCompilesWithExtraAnnotationsAndUnknownFields() throws Excepti
// Arrange
File crd = getCRD("crontab-crd.yml");
File dest = tmpFolder.newFolder("crontab-extra-annot");
CRGeneratorRunner runner = new CRGeneratorRunner(new Config(null, null, null, true, true, null));
CRGeneratorRunner runner = new CRGeneratorRunner(new Config(null, null, null, true, true, null, false));

// Act
runner.run(crd, dest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ void testEmptyObject() {
@Test
void testEmptyObjectWithSuffix() {
// Arrange
Config config = new Config(null, null, Config.Suffix.ALWAYS, null, null, null);
Config config = new Config(null, null, Config.Suffix.ALWAYS, null, null, null, true);
JObject obj = new JObject(
"v1alpha1",
"t",
Expand Down Expand Up @@ -368,6 +368,47 @@ void testObjectWithRequiredField() {
assertTrue(clz.get().getFieldByName("o1").get().getAnnotationByName("Required").isPresent());
}

@Test
void testObjectWithAndWithoutGeneratedAnnotation() {
// Arrange
JObject obj1 = new JObject(
"v1alpha1",
"t",
new HashMap<>(),
new ArrayList<>(),
false,
"",
"",
defaultConfig,
null,
Boolean.FALSE,
null);
Config config = new Config(null, null, Config.Suffix.ALWAYS, null, null, null, false);
JObject obj2 = new JObject(
"v1alpha1",
"t",
new HashMap<>(),
new ArrayList<>(),
false,
"",
"",
config,
null,
Boolean.FALSE,
null);

// Act
GeneratorResult res1 = obj1.generateJava();
GeneratorResult res2 = obj2.generateJava();

// Assert
Optional<ClassOrInterfaceDeclaration> clz1 = res1.getTopLevelClasses().get(0).getCompilationUnit().getClassByName("T");
assertTrue(clz1.get().getAnnotationByName(AbstractJSONSchema2Pojo.GENERATED_ANNOTATION.getNameAsString()).isPresent());

Optional<ClassOrInterfaceDeclaration> clz2 = res2.getTopLevelClasses().get(0).getCompilationUnit().getClassByName("T");
assertFalse(clz2.get().getAnnotationByName(AbstractJSONSchema2Pojo.GENERATED_ANNOTATION.getNameAsString()).isPresent());
}

@Test
void testDefaultEnum() {
// Arrange
Expand Down Expand Up @@ -418,7 +459,7 @@ void testNotUppercaseEnum() {
JEnum enu = new JEnum(
"t",
enumValues,
new Config(false, null, null, null, null, null),
new Config(false, null, null, null, null, null, true),
null,
Boolean.FALSE,
null);
Expand Down Expand Up @@ -538,7 +579,7 @@ void testObjectOfObjects() {
@Test
void testObjectOfObjectsWithTopLevelPrefix() {
// Arrange
Config config = new Config(null, Config.Prefix.TOP_LEVEL, null, null, null, null);
Config config = new Config(null, Config.Prefix.TOP_LEVEL, null, null, null, null, true);
Map<String, JSONSchemaProps> props = new HashMap<>();
JSONSchemaProps newObj = new JSONSchemaProps();
newObj.setType("object");
Expand Down Expand Up @@ -568,7 +609,7 @@ void testObjectOfObjectsWithTopLevelPrefix() {
@Test
void testObjectOfObjectsWithAlwaysPrefix() {
// Arrange
Config config = new Config(null, Config.Prefix.ALWAYS, null, null, null, null);
Config config = new Config(null, Config.Prefix.ALWAYS, null, null, null, null, true);
Map<String, JSONSchemaProps> props = new HashMap<>();
JSONSchemaProps newObj = new JSONSchemaProps();
newObj.setType("object");
Expand Down