Skip to content

2459 mirroredtypeexceptions occur when using config generator annotations #2465

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

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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class ApiKeyConfig {

@ArrayField(
configFieldName = "pathPrefixAuths",
externalized = true,
items = ApiKey.class
)
List<ApiKey> pathPrefixAuths;
Expand Down
2 changes: 1 addition & 1 deletion apikey-config/src/main/resources/config/apikey.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ enabled: ${apikey.enabled:false}
#The default value is false. If you want to enable it, you need to use the following repo
#https://github.com/networknt/light-hash command line tool to hash the clear text key.
hashEnabled: ${apikey.hashEnabled:false}
pathPrefixAuths: 'null'
pathPrefixAuths: ${apikey.pathPrefixAuths:}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,7 @@
*
* @author Steve Hu
*/
@ConfigSchema(
configKey = "audit",
outputFormats = {
OutputFormat.JSON_SCHEMA,
OutputFormat.YAML
}
)
@ConfigSchema(configKey = "audit", outputFormats = {OutputFormat.JSON_SCHEMA, OutputFormat.YAML})
public class AuditConfig {
private static final Logger logger = LoggerFactory.getLogger(AuditConfig.class);

Expand Down
6 changes: 2 additions & 4 deletions audit-config/src/main/resources/config/audit.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,12 @@
"requestBodyMaxSize" : {
"type" : "integer",
"description" : "The limit of the request body to put into the audit entry if requestBody is in the list of audit. If the\nrequest body is bigger than the max size, it will be truncated to the max size. The default value is 4096.",
"default" : 4096,
"format" : "INT32"
"default" : 4096
},
"responseBodyMaxSize" : {
"type" : "integer",
"description" : "The limit of the response body to put into the audit entry if responseBody is in the list of audit. If the\nresponse body is bigger than the max size, it will be truncated to the max size. The default value is 4096.",
"default" : 4096,
"format" : "INT32"
"default" : 4096
},
"enabled" : {
"type" : "boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.lang.annotation.*;

@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ArrayField {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.lang.annotation.*;

@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BooleanField {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
Expand All @@ -26,7 +34,7 @@ public class ConfigAnnotationParser extends AbstractProcessor {
private ProcessingEnvironment processingEnv;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
public synchronized void init(final ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.processingEnv = processingEnv;
this.metadataParser = new MetadataParser();
Expand All @@ -35,27 +43,86 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
LOG.trace("Processing annotations");

public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
final var configs = roundEnv.getElementsAnnotatedWith(ConfigSchema.class);

if (roundEnv.processingOver()) {
return true;
}

for (final var config : configs) {

final var configClassMetadata = config.getAnnotation(ConfigSchema.class);
final var configName = configClassMetadata.configKey();
final var outputs = configClassMetadata.outputFormats();
final var outputDir = configClassMetadata.outputDir();
final var configMetadata = this.metadataParser.parseMetadata(config, this.processingEnv);

for (final var output : outputs) {
/* Get config path inside project folder */
final var modulePath = this.getPathInCurrentModule("../../src/main/resources/config/", 1);

LOG.debug("Generating {} file for: {}", output.toString(), config.getSimpleName());
/* Get config path inside target folder */
final var targetPathMirror = this.getPathInCurrentModule("config/", 2);

Generator.getGenerator(output, configName).writeSchemaToFile(outputDir, configMetadata);
/* Generate a file inside the project folder and inside the target folder. */
final var configMetadata = this.metadataParser.parseMetadata(config, this.processingEnv);
for (final var output : configClassMetadata.outputFormats()) {
final var extension = output.getExtension();

/* write config in project folder. */
final var projectFile = this.resolveOrCreateFile(modulePath, configClassMetadata.configKey() + extension).toPath();
try (var writer = Files.newBufferedWriter(projectFile)) {
Generator.getGenerator(output, configClassMetadata.configKey())
.writeSchemaToFile(writer, configMetadata);
} catch (IOException e) {
throw new RuntimeException("Error generating config file", e);
}

/* write config in target folder. */
final var targetFile = this.resolveOrCreateFile(targetPathMirror, configClassMetadata.configKey() + extension).toPath();
try (var writer = Files.newBufferedWriter(targetFile)) {
Generator.getGenerator(output, configClassMetadata.configKey())
.writeSchemaToFile(writer, configMetadata);
} catch (IOException e) {
throw new RuntimeException("Error generating config file", e);
}
}

}
return true;
}

/**
* Resolves a file in the given path, if the file does not exist, it will be created.
* @param path The path to the file.
* @param fileName The name of the file.
* @return The resolved file.
*/
private File resolveOrCreateFile(final Path path, final String fileName) {
final var file = path.resolve(fileName).toFile();

if (!file.exists()) {
try {

if (file.createNewFile())
LOG.debug("File {} created.", file.getName());


else LOG.warn("File {} already exists, the existing file will have it's contents overwritten.", file.getName());
} catch (IOException e) {
throw new RuntimeException("Could not create a new file '" + fileName + "', for the directory '" + file + "'. " + e.getMessage());
}
}

return file;
}


private Path getPathInCurrentModule(final String relativeModulePath, final int anchorIndex) {
final var path = Objects.requireNonNullElse(relativeModulePath, "");
final FileObject resource;
try {
resource = this.processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "anchor" + anchorIndex, (Element[]) null);
} catch (IOException e) {
throw new RuntimeException("Could not create temp resource to find the current module", e);
}
return Paths.get(resource.toUri()).getParent().resolve(path);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.lang.annotation.*;


@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IntegerField {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.type.MirroredTypeException;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Optional;
Expand Down Expand Up @@ -165,7 +166,13 @@ private static Optional<LinkedHashMap<String, Object>> getObjectPropertyMetadata
* @return A LinkedHashMap containing the metadata.
*/
private static LinkedHashMap<String, Object> parseArrayMetadata(final ArrayField field, final ProcessingEnvironment processingEnvironment) {
final var itemElement = ReflectionUtils.safeGetElement(field.items().getCanonicalName(), processingEnvironment);
String canonicalName;
try {
canonicalName = field.items().getCanonicalName();
} catch (MirroredTypeException e) {
canonicalName = e.getTypeMirrors().get(0).toString();
}
final var itemElement = ReflectionUtils.safeGetElement(canonicalName, processingEnvironment);
final var itemMetadata = new LinkedHashMap<String, Object>();
gatherObjectSchemaData(itemElement, itemMetadata, processingEnvironment);

Expand Down Expand Up @@ -257,7 +264,13 @@ private static LinkedHashMap<String, Object> parseStringMetadata(final StringFie
* @return A LinkedHashMap containing the metadata.
*/
private static LinkedHashMap<String, Object> parseObjectMetadata(final ObjectField field, final ProcessingEnvironment processingEnvironment) {
final var refElement = ReflectionUtils.safeGetElement(field.ref().getCanonicalName(), processingEnvironment);
String canonicalName;
try {
canonicalName = field.ref().getCanonicalName();
} catch (MirroredTypeException e) {
canonicalName = e.getTypeMirrors().get(0).toString();
}
final var refElement = ReflectionUtils.safeGetElement(canonicalName, processingEnvironment);
final var refMetadata = new LinkedHashMap<String, Object>();
gatherObjectSchemaData(refElement, refMetadata, processingEnvironment);
final var metadata = new LinkedHashMap<String, Object>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.lang.annotation.*;

@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NullField {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.lang.annotation.*;

@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NumberField {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.lang.annotation.*;


@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectField {
Expand Down
16 changes: 13 additions & 3 deletions config/src/main/java/com/networknt/config/schema/OutputFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,25 @@ public enum OutputFormat {
/**
* Draft 07 JSON schema output.
*/
JSON_SCHEMA,
JSON_SCHEMA(".json"),

/**
* Light4J YAML configuration output.
*/
YAML,
YAML(".yaml"),

/**
* Config generator debug output (just dumps the annotation processing results)
*/
DEBUG
DEBUG(".debug.json");

final String extension;

OutputFormat(String extension) {
this.extension = extension;
}

public String getExtension() {
return extension;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.Optional;

public class ReflectionUtils {
Expand Down Expand Up @@ -81,4 +83,20 @@ public static <A extends Annotation> Optional<A> safeGetAnnotation(final Element
else return Optional.of(annotation);
}
}

public static String getResourceFolderForConfigClass(Class<?> configClass) {
URL url = configClass.getResource("/config");

if (url == null)
throw new RuntimeException("No resource folder found for class: " + configClass.getSimpleName());

File file;
try {
file = new File(url.toURI());
} catch (Exception e) {
file = new File(url.getPath());
}

return file.getAbsolutePath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.lang.annotation.*;

@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StringField {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.tools.FileObject;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.LinkedHashMap;

/**
Expand All @@ -20,10 +24,19 @@ public DebugGenerator(final String configKey) {
}

@Override
public void writeSchemaToFile(String path, LinkedHashMap<String, Object> metadata) {
public void writeSchemaToFile(final FileObject path, LinkedHashMap<String, Object> metadata) throws IOException {
writeSchemaToFile(path.openOutputStream(), metadata);
}

@Override
public void writeSchemaToFile(final Writer writer, final LinkedHashMap<String, Object> metadata) throws IOException {
this.objectWriter.writerWithDefaultPrettyPrinter().writeValue(writer, metadata);
}

@Override
public void writeSchemaToFile(final OutputStream os, final LinkedHashMap<String, Object> metadata) {
try {
final var file = new File(path + "/" + configKey + ".debug.json");
this.objectWriter.writerWithDefaultPrettyPrinter().writeValue(file, metadata);
this.objectWriter.writerWithDefaultPrettyPrinter().writeValue(os, metadata);
} catch (Exception e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.tools.FileObject;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Objects;

Expand Down Expand Up @@ -74,7 +78,10 @@ protected static boolean fieldIsHashMap(final LinkedHashMap<String, Object> map,
* @param path The path to write the schema to.
* @param metadata The metadata to write.
*/
public abstract void writeSchemaToFile(final String path, final LinkedHashMap<String, Object> metadata);
public abstract void writeSchemaToFile(final FileObject path, final LinkedHashMap<String, Object> metadata) throws IOException;
public abstract void writeSchemaToFile(final Writer writer, final LinkedHashMap<String, Object> metadata) throws IOException;

public abstract void writeSchemaToFile(final OutputStream os, final LinkedHashMap<String, Object> metadata);

/**
* Parses an array field.
Expand Down
Loading