From 0712b1351f0364ae819a5caad6e9aa60e3d93c00 Mon Sep 17 00:00:00 2001 From: imesh94 Date: Tue, 16 Jul 2024 14:55:56 +0530 Subject: [PATCH 1/3] Fix performance issue in schema validation --- .../handlers/security/SchemaValidator.java | 28 ++++++++++++++++--- .../security/model/OpenAPIRequest.java | 12 ++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java index 2b857a0feba9..7f73907920a7 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java +++ b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.swagger.util.Json; import io.swagger.v3.oas.models.OpenAPI; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.MessageContext; @@ -31,6 +32,8 @@ import org.wso2.carbon.apimgt.gateway.handlers.security.model.OpenAPIResponse; import org.wso2.carbon.apimgt.gateway.utils.GatewayUtils; +import java.util.HashMap; + /** * This SchemaValidator handler validates the request/response messages against schema defined in the swagger. */ @@ -40,6 +43,7 @@ public class SchemaValidator extends AbstractHandler { private static final Log logger = LogFactory.getLog(SchemaValidator.class); private static final String HTTP_SC_CODE = "400"; public static final String REG_TIME_MODULE = "register.timeModule"; + public static HashMap validatorMap = new HashMap<>(); /** * Method to generate OpenApiInteractionValidator when the openAPI is provided. @@ -69,8 +73,16 @@ public boolean handleRequest(MessageContext messageContext) { } logger.debug("Validating the API request Body content.."); OpenAPI openAPI = (OpenAPI) messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_OBJECT); - if (openAPI != null) { - OpenApiInteractionValidator validator = getOpenAPIValidator(openAPI); + Object openAPIStringObject = messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_STRING); + if (openAPI != null && openAPIStringObject != null) { + OpenApiInteractionValidator validator; + String openAPIIdentifier = DigestUtils.md5Hex(openAPIStringObject.toString()); + if (validatorMap.containsKey(openAPIIdentifier)) { + validator = validatorMap.get(openAPIIdentifier); + } else { + validator = getOpenAPIValidator(openAPI); + validatorMap.put(openAPIIdentifier, validator); + } OpenAPIRequest request = new OpenAPIRequest(messageContext); ValidationReport validationReport = validator.validateRequest(request); @@ -92,8 +104,16 @@ public boolean handleRequest(MessageContext messageContext) { public boolean handleResponse(MessageContext messageContext) { OpenAPI openAPI = (OpenAPI) messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_OBJECT); - if (openAPI != null) { - OpenApiInteractionValidator validator = getOpenAPIValidator(openAPI); + Object openAPIStringObject = messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_STRING); + if (openAPI != null && openAPIStringObject != null) { + OpenApiInteractionValidator validator; + String openAPIIdentifier = DigestUtils.md5Hex(openAPIStringObject.toString()); + if (validatorMap.containsKey(openAPIIdentifier)) { + validator = validatorMap.get(openAPIIdentifier); + } else { + validator = getOpenAPIValidator(openAPI); + validatorMap.put(openAPIIdentifier, validator); + } OpenAPIResponse response = new OpenAPIResponse(messageContext); ValidationReport validationReport = validator.validateResponse(response.getPath(), response.getMethod(), diff --git a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/model/OpenAPIRequest.java b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/model/OpenAPIRequest.java index 627be5ba03b1..ac7594eec13e 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/model/OpenAPIRequest.java +++ b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/model/OpenAPIRequest.java @@ -19,11 +19,8 @@ import com.atlassian.oai.validator.model.Request; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -import io.swagger.parser.OpenAPIParser; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.parser.core.models.ParseOptions; -import io.swagger.v3.parser.core.models.SwaggerParseResult; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.MessageContext; @@ -33,7 +30,6 @@ import org.wso2.carbon.apimgt.gateway.handlers.security.utils.SchemaValidationUtils; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Locale; @@ -73,12 +69,8 @@ public OpenAPIRequest(MessageContext messageContext) { //Set Request path path = SchemaValidationUtils.getRestSubRequestPath( messageContext.getProperty(REST_SUB_REQUEST_PATH).toString()); - String swagger = messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_STRING).toString(); - if (swagger != null) { - OpenAPIParser openAPIParser = new OpenAPIParser(); - SwaggerParseResult swaggerParseResult = - openAPIParser.readContents(swagger, new ArrayList<>(), new ParseOptions()); - OpenAPI openAPI = swaggerParseResult.getOpenAPI(); + OpenAPI openAPI = (OpenAPI) messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_OBJECT); + if (openAPI != null) { validatePath(openAPI); } //extract transport headers From 49e0c7bea11dfdb9fc8564b51ff27150244cf2b1 Mon Sep 17 00:00:00 2001 From: imesh94 Date: Tue, 16 Jul 2024 15:13:55 +0530 Subject: [PATCH 2/3] Add comments --- .../apimgt/gateway/handlers/security/SchemaValidator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java index 7f73907920a7..84133483ad4e 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java +++ b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java @@ -33,7 +33,6 @@ import org.wso2.carbon.apimgt.gateway.utils.GatewayUtils; import java.util.HashMap; - /** * This SchemaValidator handler validates the request/response messages against schema defined in the swagger. */ @@ -77,6 +76,7 @@ public boolean handleRequest(MessageContext messageContext) { if (openAPI != null && openAPIStringObject != null) { OpenApiInteractionValidator validator; String openAPIIdentifier = DigestUtils.md5Hex(openAPIStringObject.toString()); + // Use existing validator if present if (validatorMap.containsKey(openAPIIdentifier)) { validator = validatorMap.get(openAPIIdentifier); } else { @@ -106,6 +106,7 @@ public boolean handleResponse(MessageContext messageContext) { OpenAPI openAPI = (OpenAPI) messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_OBJECT); Object openAPIStringObject = messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_STRING); if (openAPI != null && openAPIStringObject != null) { + // Use existing validator if present OpenApiInteractionValidator validator; String openAPIIdentifier = DigestUtils.md5Hex(openAPIStringObject.toString()); if (validatorMap.containsKey(openAPIIdentifier)) { From 395f36b59b1108e07e5fbe4382df371f23c3255b Mon Sep 17 00:00:00 2001 From: imesh94 Date: Tue, 16 Jul 2024 20:00:49 +0530 Subject: [PATCH 3/3] Move get validator logic to a separate method --- .../handlers/security/SchemaValidator.java | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java index 84133483ad4e..3b505dd5a143 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java +++ b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/security/SchemaValidator.java @@ -33,6 +33,7 @@ import org.wso2.carbon.apimgt.gateway.utils.GatewayUtils; import java.util.HashMap; + /** * This SchemaValidator handler validates the request/response messages against schema defined in the swagger. */ @@ -74,17 +75,8 @@ public boolean handleRequest(MessageContext messageContext) { OpenAPI openAPI = (OpenAPI) messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_OBJECT); Object openAPIStringObject = messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_STRING); if (openAPI != null && openAPIStringObject != null) { - OpenApiInteractionValidator validator; - String openAPIIdentifier = DigestUtils.md5Hex(openAPIStringObject.toString()); - // Use existing validator if present - if (validatorMap.containsKey(openAPIIdentifier)) { - validator = validatorMap.get(openAPIIdentifier); - } else { - validator = getOpenAPIValidator(openAPI); - validatorMap.put(openAPIIdentifier, validator); - } + OpenApiInteractionValidator validator = getOpenApiValidator(openAPI, openAPIStringObject.toString()); OpenAPIRequest request = new OpenAPIRequest(messageContext); - ValidationReport validationReport = validator.validateRequest(request); messageContext.setProperty(APIMgtGatewayConstants.SCHEMA_VALIDATION_REPORT, validationReport); if (validationReport.hasErrors()) { @@ -106,17 +98,8 @@ public boolean handleResponse(MessageContext messageContext) { OpenAPI openAPI = (OpenAPI) messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_OBJECT); Object openAPIStringObject = messageContext.getProperty(APIMgtGatewayConstants.OPEN_API_STRING); if (openAPI != null && openAPIStringObject != null) { - // Use existing validator if present - OpenApiInteractionValidator validator; - String openAPIIdentifier = DigestUtils.md5Hex(openAPIStringObject.toString()); - if (validatorMap.containsKey(openAPIIdentifier)) { - validator = validatorMap.get(openAPIIdentifier); - } else { - validator = getOpenAPIValidator(openAPI); - validatorMap.put(openAPIIdentifier, validator); - } + OpenApiInteractionValidator validator = getOpenApiValidator(openAPI, openAPIStringObject.toString()); OpenAPIResponse response = new OpenAPIResponse(messageContext); - ValidationReport validationReport = validator.validateResponse(response.getPath(), response.getMethod(), response); if (validationReport.hasErrors()) { @@ -131,4 +114,26 @@ public boolean handleResponse(MessageContext messageContext) { } return true; } + + /** + * Method to get OpenApiInteractionValidator for the given openAPI. + * If the validator is already created for the given openAPI, it will return the existing one. + * Created validators are mapped against the hash of the yaml. + * + * @param openAPI - OpenAPI object + * @param openAPIString - OpenAPI string (yaml) + * @return OpenApiInteractionValidator + */ + private OpenApiInteractionValidator getOpenApiValidator(OpenAPI openAPI, String openAPIString) { + + OpenApiInteractionValidator validator; + String apiIdentifier = DigestUtils.md5Hex(openAPIString); + if (validatorMap.containsKey(apiIdentifier)) { + validator = validatorMap.get(apiIdentifier); + } else { + validator = getOpenAPIValidator(openAPI); + validatorMap.put(apiIdentifier, validator); + } + return validator; + } }