Skip to content

Commit f34b75e

Browse files
committed
Initial implementation
1 parent a762390 commit f34b75e

File tree

11 files changed

+177
-45
lines changed

11 files changed

+177
-45
lines changed

.github/workflows/deploy.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: deploy commits
2+
on:
3+
push: { }
4+
pull_request: { }
5+
6+
jobs:
7+
build:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: eitco/maven-deploy@0.0.2
11+
with:
12+
gpg-private-key: ${{ secrets.ARTIFACT_SIGNING_GPG_PRIVATE_KEY }}
13+
gpg-key-name: ${{ secrets.ARTIFACT_SIGNING_GPG_KEY_NAME }}
14+
gpg-passphrase: ${{ secrets.ARTIFACT_SIGNING_GPG_PRIVATE_KEY_PASSWORD }}
15+
deploy-user: ${{ secrets.DEPLOY_USER_GITHUB_PACKAGES }}
16+
deploy-password: ${{ secrets.DEPLOY_TOKEN_GITHUB_PACKAGES }}

.github/workflows/release.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: release
2+
3+
on:
4+
workflow_dispatch
5+
6+
jobs:
7+
release:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: eitco/maven-release@0.0.2
11+
with:
12+
gpg-private-key: ${{ secrets.ARTIFACT_SIGNING_GPG_PRIVATE_KEY }}
13+
gpg-key-name: ${{ secrets.ARTIFACT_SIGNING_GPG_KEY_NAME }}
14+
gpg-passphrase: ${{ secrets.ARTIFACT_SIGNING_GPG_PRIVATE_KEY_PASSWORD }}
15+
deploy-user: ${{ secrets.OSSRH_JIRA_USERNAME }}
16+
deploy-password: ${{ secrets.OSSRH_JIRA_PASSWORD }}

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@
66

77
# additional-spring-configuration-metadata-processor
88

9-
This [java annotation processor](https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html) generates additional [spring configuration metadata](https://docs.spring.io/spring-boot/specification/configuration-metadata/annotation-processor.html#appendix.configuration-metadata.annotation-processor.adding-additional-metadata)
10-
for collections in classes annotated with `@ConfigurationProperties`. This metadata is not required for IDE support.
11-
It's main usage is providing data for the [configuration documentation generation plugin](https://github.com/eitco/spring-config-collector-maven-plugin).
9+
This [java annotation processor](https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html) generates additional spring configuration metadata for the
10+
[configuration documentation generation plugin](https://github.com/eitco/spring-config-collector-maven-plugin).

pom.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
<version>0.0.19</version>
1111
</parent>
1212

13-
<artifactId>additional-spring-configuration-metadata-processor</artifactId>
13+
<artifactId>documentation-configuration-metadata-processor</artifactId>
1414
<version>1.0.0-SNAPSHOT</version>
1515

1616
<properties>
1717
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1818
<spring.version>5.3.9</spring.version>
19+
<spring-boot.version>3.4.3</spring-boot.version>
1920
</properties>
2021

2122
<developers>
@@ -29,7 +30,12 @@
2930
<dependency>
3031
<groupId>org.springframework.boot</groupId>
3132
<artifactId>spring-boot-configuration-processor</artifactId>
32-
<version>3.4.3</version>
33+
<version>${spring-boot.version}</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot</artifactId>
38+
<version>${spring-boot.version}</version>
3339
</dependency>
3440
</dependencies>
3541

src/it/simple/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
<version>@project.version@</version>
1616
<optional>true</optional>
1717
</dependency>
18+
<dependency>
19+
<groupId>org.springframework.boot</groupId>
20+
<artifactId>spring-boot</artifactId>
21+
<version>3.4.3</version>
22+
</dependency>
1823
</dependencies>
1924

2025

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package de.eitco.cicd.ascmp.test;
2+
3+
public class TestNested {
4+
5+
/**
6+
* This is my nested setting.
7+
*/
8+
private int myNestedSetting = 7;
9+
10+
public int getMyNestedSetting() {
11+
return myNestedSetting;
12+
}
13+
14+
public void setMyNestedSetting(int myNestedSetting) {
15+
this.myNestedSetting = myNestedSetting;
16+
}
17+
}
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
package de.eitco.cicd.ascmp.test;
22

33
import de.eitco.cicd.ascmp.AdditionalConfigurationMetadata;
4+
import org.springframework.boot.context.properties.NestedConfigurationProperty;
45

5-
@AdditionalConfigurationMetadata(group = "TestGroup")
6+
@AdditionalConfigurationMetadata(groups = {"TestGroup1","TestGroup2"})
67
public class TestSettings {
78

89
/**
910
* This is my first setting.
1011
*/
1112
private String myFirstSetting = "myFirstSetting";
1213

14+
@NestedConfigurationProperty
15+
private TestNested nested = new TestNested();
16+
1317
public String getMyFirstSetting() {
1418
return myFirstSetting;
1519
}
1620

1721
public void setMyFirstSetting(String myFirstSetting) {
1822
this.myFirstSetting = myFirstSetting;
1923
}
24+
25+
public TestNested getNested() {
26+
return nested;
27+
}
28+
29+
public void setNested(TestNested nested) {
30+
this.nested = nested;
31+
}
2032
}

src/it/simple/verify.groovy

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import java.nio.file.Files
22

3-
File baseDirectory = new File("$basedir");
3+
File baseDirectory = new File("$basedir")
44

5-
File generatedSourceFile = new File(baseDirectory, "target/classes/META-INF/additional-spring-configuration-metadata.json")
5+
File generatedSourceFile = new File(baseDirectory, "target/classes/META-INF/documentation-spring-configuration-metadata.json")
66
assert generatedSourceFile.exists()
77
assert generatedSourceFile.isFile()
88

99
String content = Files.readString(generatedSourceFile.toPath())
1010

1111
assert content.contains("de.eitco.cicd.ascmp.test.TestSettings")
12-
12+
assert content.contains("test-group1.nested.my-nested-setting")
13+
assert content.contains("test-group2.nested.my-nested-setting")

src/main/java/de/eitco/cicd/ascmp/AdditionalConfigurationMetadata.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
@Target(ElementType.TYPE)
1010
public @interface AdditionalConfigurationMetadata {
1111

12-
String group();
12+
String[] groups();
1313
}

src/main/java/de/eitco/cicd/ascmp/AdditionalMetadataProcessor.java

Lines changed: 89 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,35 @@
66
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
77
import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller;
88
import org.springframework.boot.configurationprocessor.support.ConventionUtils;
9+
import org.springframework.boot.context.properties.NestedConfigurationProperty;
910

1011
import javax.annotation.processing.AbstractProcessor;
1112
import javax.annotation.processing.ProcessingEnvironment;
1213
import javax.annotation.processing.RoundEnvironment;
1314
import javax.annotation.processing.SupportedSourceVersion;
1415
import javax.lang.model.SourceVersion;
16+
import javax.lang.model.element.Element;
1517
import javax.lang.model.element.ElementKind;
1618
import javax.lang.model.element.TypeElement;
1719
import javax.lang.model.element.VariableElement;
20+
import javax.lang.model.type.TypeKind;
21+
import javax.lang.model.type.TypeMirror;
1822
import javax.tools.FileObject;
1923
import javax.tools.StandardLocation;
2024
import java.io.IOException;
2125
import java.io.OutputStream;
26+
import java.util.HashSet;
2227
import java.util.List;
2328
import java.util.Map;
2429
import java.util.Set;
2530

2631
@SupportedSourceVersion(SourceVersion.RELEASE_21)
2732
public class AdditionalMetadataProcessor extends AbstractProcessor {
2833

29-
private static final String ADDITIONAL_METADATA_PATH = "META-INF/additional-spring-configuration-metadata.json";
34+
private static final String ADDITIONAL_METADATA_PATH = "META-INF/documentation-spring-configuration-metadata.json";
3035

3136
private final ConfigurationMetadata configurationMetadata = new ConfigurationMetadata();
37+
private final Set<ElementWithGroup> processedElements = new HashSet<>();
3238

3339
private FieldValuesParser fieldValuesParser;
3440

@@ -55,53 +61,97 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
5561

5662
roundEnv.getElementsAnnotatedWith(AdditionalConfigurationMetadata.class).forEach(element -> {
5763

58-
String group = element.getAnnotation(AdditionalConfigurationMetadata.class).group();
59-
String elementType = ((TypeElement) element).getQualifiedName().toString();
64+
String[] groups = element.getAnnotation(AdditionalConfigurationMetadata.class).groups();
6065

61-
Map<String, Object> fieldValues;
66+
for (String group : groups) {
6267

63-
try {
64-
fieldValues = fieldValuesParser.getFieldValues((TypeElement) element);
65-
} catch (Exception e) {
66-
throw new RuntimeException(e);
67-
}
68+
String elementType = ((TypeElement) element).getQualifiedName().toString();
69+
70+
configurationMetadata.addIfMissing(ItemMetadata.newGroup(ConventionUtils.toDashedCase(group), elementType, elementType, null));
6871

69-
configurationMetadata.add(ItemMetadata.newGroup(ConventionUtils.toDashedCase(group), elementType, elementType, null));
70-
71-
List<VariableElement> variableElements = element.getEnclosedElements().stream()
72-
.filter(e -> e.getKind() == ElementKind.FIELD)
73-
.map(e -> (VariableElement) e)
74-
.toList();
75-
76-
for (VariableElement variableElement : variableElements) {
77-
78-
String fieldName = variableElement.getSimpleName().toString();
79-
String name = group + "." + fieldName;
80-
String docComment = getDocComment(variableElement);
81-
82-
configurationMetadata.add(
83-
ItemMetadata.newProperty(
84-
null,
85-
name,
86-
variableElement.asType().toString(),
87-
elementType,
88-
null,
89-
docComment,
90-
fieldValues.get(fieldName),
91-
null
92-
)
93-
);
72+
try {
73+
processElement(element, group, elementType);
74+
} catch (Exception e) {
75+
processingEnv.getMessager().printError("[AdditionalMetadataProcessor]: " + e.getMessage(), element);
76+
}
9477
}
9578
});
9679

9780
if (roundEnv.processingOver()) {
9881

99-
writeJson();
82+
try {
83+
writeJson();
84+
} catch (Exception e) {
85+
processingEnv.getMessager().printError("[AdditionalMetadataProcessor]: " + e.getMessage());
86+
}
10087
}
10188

10289
return false;
10390
}
10491

92+
private void processElement(Element element, String group, String elementType) {
93+
94+
ElementWithGroup elementWithGroup = new ElementWithGroup(group, element);
95+
96+
if (processedElements.contains(elementWithGroup)) {
97+
return;
98+
}
99+
100+
processedElements.add(elementWithGroup);
101+
102+
Map<String, Object> fieldValues;
103+
104+
try {
105+
fieldValues = fieldValuesParser.getFieldValues((TypeElement) element);
106+
} catch (Exception e) {
107+
throw new RuntimeException(e);
108+
}
109+
110+
List<VariableElement> variableElements = element.getEnclosedElements().stream()
111+
.filter(e -> e.getKind() == ElementKind.FIELD)
112+
.map(e -> (VariableElement) e)
113+
.toList();
114+
115+
for (VariableElement variableElement : variableElements) {
116+
117+
String fieldName = variableElement.getSimpleName().toString();
118+
119+
NestedConfigurationProperty nestedConfigurationProperty = variableElement.getAnnotation(NestedConfigurationProperty.class);
120+
121+
if (nestedConfigurationProperty != null) {
122+
Element nestedElement = processingEnv.getTypeUtils().asElement(variableElement.asType());
123+
processElement(nestedElement, group + "." + fieldName, elementType);
124+
} else {
125+
addProperty(group, elementType, variableElement, fieldValues, fieldName);
126+
}
127+
}
128+
}
129+
130+
private void addProperty(
131+
String group,
132+
String elementType,
133+
VariableElement variableElement,
134+
Map<String, Object> fieldValues,
135+
String fieldName
136+
) {
137+
138+
String name = group + "." + fieldName;
139+
String docComment = getDocComment(variableElement);
140+
141+
configurationMetadata.addIfMissing(
142+
ItemMetadata.newProperty(
143+
null,
144+
name,
145+
variableElement.asType().toString(),
146+
elementType,
147+
null,
148+
docComment,
149+
fieldValues.get(fieldName),
150+
null
151+
)
152+
);
153+
}
154+
105155
private void writeJson() {
106156
try {
107157
FileObject resource = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", ADDITIONAL_METADATA_PATH);
@@ -120,6 +170,10 @@ private String getDocComment(VariableElement variableElement) {
120170

121171
String docComment = processingEnv.getElementUtils().getDocComment(variableElement);
122172

173+
if (docComment == null) {
174+
return null;
175+
}
176+
123177
docComment = docComment.replaceAll("\r", " ");
124178
docComment = docComment.replaceAll("\n", " ");
125179

0 commit comments

Comments
 (0)