From af8bb8241792a03c6d00081d9d23b0937bd9083f Mon Sep 17 00:00:00 2001 From: Arthur Geweiler Date: Thu, 2 Nov 2023 11:58:40 +0100 Subject: [PATCH] feat: introduce cache loaded extensions in OpenAPIDeserializer --- .../v3/parser/processors/SchemaProcessor.java | 1 - .../v3/parser/util/OpenAPIDeserializer.java | 42 +++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/SchemaProcessor.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/SchemaProcessor.java index 6a3f480ab9..9dbee9987a 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/SchemaProcessor.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/SchemaProcessor.java @@ -47,7 +47,6 @@ public void processSchema(Schema schema) { return; } if (openapi31) { - // TODO use as singleton somewhere loaded as static used by both here and deserializer List jsonschemaExtensions = OpenAPIDeserializer.getJsonSchemaParserExtensions(); for (JsonSchemaParserExtension jsonschemaExtension: jsonschemaExtensions) { if (jsonschemaExtension.resolveSchema(schema, cache, openAPI, openapi31)) { diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java index abb24fe4f5..1b64179f49 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java @@ -264,6 +264,17 @@ public class OpenAPIDeserializer { "(\\d{2}):(\\d{2})(\\.\\d+)?((Z)|([+-]\\d{2}:\\d{2}))$"); private static final Pattern RFC3339_DATE_PATTERN = Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2})$"); private static final String REFERENCE_SEPARATOR = "#/"; + + private static final int MAX_EXTENSION_ENTRIES = 20; + + // Holds extensions to a given classloader. Implemented as a least-recently used cache + private static final Map> jsonSchemaParserExtensionMap = new LinkedHashMap>() { + @Override + protected boolean removeEldestEntry(Map.Entry> eldest) { + return size() > MAX_EXTENSION_ENTRIES; + } + }; + private Components components; private JsonNode rootNode; private Map rootMap; @@ -3780,15 +3791,30 @@ public static List getJsonSchemaParserExtensions() { return extensions; } - protected static List getJsonSchemaParserExtensions(ClassLoader cl) { - final List extensions = new ArrayList<>(); + /** + * Locates the extensions for given {@link ClassLoader} and stores them for performance reason in an in-memory + * (LRU) cache. + * + * @param cl the {@link ClassLoader} for which the extensions are located + * @return + */ + protected static List getJsonSchemaParserExtensions(ClassLoader cl) { + if (jsonSchemaParserExtensionMap.containsKey(cl)) { + return jsonSchemaParserExtensionMap.get(cl); + } - final ServiceLoader loader = ServiceLoader.load(JsonSchemaParserExtension.class, cl); - for (JsonSchemaParserExtension extension : loader) { - extensions.add(extension); - } - return extensions; - } + final List extensions = new ArrayList<>(); + final ServiceLoader loader = ServiceLoader.load(JsonSchemaParserExtension.class, cl); + for (JsonSchemaParserExtension extension : loader) { + extensions.add(extension); + } + + // don't cache null-Value classLoader (e.g. Bootstrap Classloader) + if (cl != null) { + jsonSchemaParserExtensionMap.put(cl, extensions); + } + return extensions; + } public Schema getJsonSchema(JsonNode jsonNode, String location, ParseResult result) { if (jsonNode == null) {