NetworkNTJsonschemaValidator.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */

  20. package org.openspcoop2.utils.json.validation;

  21. import java.io.ByteArrayOutputStream;
  22. import java.util.Set;

  23. import org.openspcoop2.utils.LoggerWrapperFactory;
  24. import org.openspcoop2.utils.json.IJsonSchemaValidator;
  25. import org.openspcoop2.utils.json.JSONUtils;
  26. import org.openspcoop2.utils.json.JsonSchemaValidatorConfig;
  27. import org.openspcoop2.utils.json.ValidationException;
  28. import org.openspcoop2.utils.json.ValidationResponse;
  29. import org.openspcoop2.utils.json.ValidationResponse.ESITO;
  30. import org.slf4j.Logger;

  31. import com.fasterxml.jackson.databind.JsonNode;
  32. import com.fasterxml.jackson.databind.ObjectMapper;
  33. import com.networknt.schema.JsonSchema;
  34. import com.networknt.schema.JsonSchemaFactory;
  35. import com.networknt.schema.SchemaValidatorsConfig;
  36. import com.networknt.schema.SpecVersion;
  37. import com.networknt.schema.SpecVersionDetector;
  38. import com.networknt.schema.ValidationMessage;

  39. /**
  40.  * NetworkNTJsonschemaValidator
  41.  *
  42.  * @author Giovanni Bussu (bussu@link.it)
  43.  * @author $Author$
  44.  * @version $Rev$, $Date$
  45.  */
  46. public class NetworkNTJsonschemaValidator implements IJsonSchemaValidator {

  47.     /*
  48.      * NOTA: in caso di errore 'should be valid to one and only one of the schemas'
  49.      *
  50.      * Significa che più elementi possono matchare in un oneOf. Questo succede se ad esempio non è stato definito "additionalProperties: false" in ogni oggetto riferito dal oneOf
  51.      *
  52.      * NOTA2: Aggiungendo le additionalProperties a false, se si aggiungono male e non si ha un match con nessun oggetto, viene ritornato l'errore relativo ad uno dei tre a caso.
  53.      * */
  54.    
  55.     private JsonSchema schema;
  56.     private byte[] schemaBytes;
  57.     private JsonNode jsonSchema;
  58.    
  59.     private ObjectMapper mapper;
  60.     private Logger log;
  61.     private boolean logError;

  62.     // Tramite un cast e' possibile cosi personalizzare lo schema validator con le opzioni di NT
  63.     private SchemaValidatorsConfig schemaValidatorConfig = null;
  64.     public SchemaValidatorsConfig getSchemaValidatorConfig() {
  65.         return this.schemaValidatorConfig;
  66.     }
  67.     public void setSchemaValidatorConfig(SchemaValidatorsConfig schemaValidatorConfig) {
  68.         this.schemaValidatorConfig = schemaValidatorConfig;
  69.     }
  70.    
  71.     /**
  72.      *
  73.      */
  74.     public NetworkNTJsonschemaValidator() {
  75.         this.mapper = new ObjectMapper();
  76.     }
  77.     @Override
  78.     public void setSchema(byte[] schema, JsonSchemaValidatorConfig config, Logger log) throws ValidationException {
  79.        
  80.         this.log = log;
  81.         if(this.log==null) {
  82.             this.log = LoggerWrapperFactory.getLogger(NetworkNTJsonschemaValidator.class);
  83.         }
  84.         this.logError = config!=null ? config.isEmitLogError() : true;
  85.         this.schemaBytes = schema;
  86.        
  87.         try {
  88.             JsonNode jsonSchema =  this.mapper.readTree(schema);
  89.            
  90.             SpecVersion.VersionFlag version = null;
  91.             if(config!=null && config.getJsonSchemaVersion()!=null) {
  92.                 version = config.getJsonSchemaVersion();
  93.             }
  94.             else {
  95.                 version = SpecVersionDetector.detect(jsonSchema);
  96.             }
  97.            
  98.             JsonSchemaFactory factory = JsonSchemaFactory.getInstance(version);
  99.            
  100.             if(config!=null) {
  101.                 switch(config.getAdditionalProperties()) {
  102.                 case DEFAULT:
  103.                     break;
  104.                 case FORCE_DISABLE: ValidationUtils.disableAdditionalProperties(this.mapper, jsonSchema, true, true);
  105.                     break;
  106.                 case FORCE_STRING: ValidationUtils.disableAdditionalProperties(this.mapper, jsonSchema, false, true);
  107.                     break;
  108.                 case IF_NULL_DISABLE: ValidationUtils.disableAdditionalProperties(this.mapper, jsonSchema, true, false);
  109.                     break;
  110.                 case IF_NULL_STRING: ValidationUtils.disableAdditionalProperties(this.mapper, jsonSchema, false, false);
  111.                     break;
  112.                 default:
  113.                     break;
  114.                 }
  115.             }
  116.            
  117.             if(config!=null) {
  118.                 switch(config.getPoliticaInclusioneTipi()) {
  119.                 case DEFAULT:
  120.                     break;
  121.                 case ALL: ValidationUtils.addTypes(this.mapper, jsonSchema, config.getTipi(), true);
  122.                     break;
  123.                 case ANY: ValidationUtils.addTypes(this.mapper, jsonSchema, config.getTipi(), false);
  124.                     break;
  125.                 default:
  126.                     break;
  127.                 }
  128.             }

  129.             if(config!=null && config.isVerbose()) {
  130.                 try {
  131.                     ByteArrayOutputStream bout = new ByteArrayOutputStream();
  132.                     JSONUtils.getInstance(true).writeTo(jsonSchema, bout);
  133.                     bout.flush();
  134.                     bout.close();
  135.                     this.log.debug("JSON Schema: "+bout.toString());
  136.                 }catch(Exception e) {
  137.                     this.log.debug("JSON Schema build error: "+e.getMessage(),e);
  138.                 }
  139.             }
  140.            
  141.             this.jsonSchema = jsonSchema;
  142.             /*
  143.              * https://github.com/networknt/json-schema-validator/blob/master/doc/config.md
  144.              *
  145.              * SchemaValidatorsConfig configS = new SchemaValidatorsConfig();
  146.              * configS.setHandleNullableField(false);
  147.              * configS.setTypeLoose(true);
  148.              **/
  149.             if(this.schemaValidatorConfig!=null) {
  150.                 this.schema = factory.getSchema(jsonSchema, this.schemaValidatorConfig);
  151.             }
  152.             else {
  153.                 this.schema = factory.getSchema(jsonSchema);
  154.             }
  155.            
  156.         } catch(Throwable e) {
  157.             throw new ValidationException(e);
  158.         }
  159.     }
  160.    



  161.     @Override
  162.     public ValidationResponse validate(byte[] rawObject) throws ValidationException {
  163.        
  164.         ValidationResponse response = new ValidationResponse();
  165.         try {
  166.             boolean expectedString = false;
  167.             if(this.jsonSchema.has("type")) {
  168.                 try {
  169.                     JsonNode type = this.jsonSchema.get("type");
  170.                     String vType = type.asText();
  171.                     expectedString = "string".equals(vType);
  172.                 }catch(Exception e) {}
  173.             }
  174.            
  175.             JsonNode node = null;
  176.             try {
  177.                 if(expectedString) {
  178.                     node = this.mapper.getNodeFactory().textNode(new String(rawObject));
  179.                 }
  180.                 else {
  181.                     node = this.mapper.readTree(rawObject);
  182.                 }
  183.             }
  184.             catch(Exception e) {
  185.                 this.log.error(e.getMessage(),e);
  186.                 String messageString = "Read rawObject as jsonNode failed: "+e.getMessage();
  187.                 response.setEsito(ESITO.KO);
  188.                 if(this.logError) {
  189.                     ValidationUtils.logError(this.log, messageString.toString(), rawObject, this.schemaBytes, this.jsonSchema);
  190.                 }
  191.                 response.setException(new Exception(messageString.toString()));
  192.             }

  193.             if(node!=null) {
  194.                 Set<ValidationMessage> validate = this.schema.validate(node);
  195.                 if(validate.isEmpty()) {
  196.                     response.setEsito(ESITO.OK);    
  197.                 } else {
  198.                     response.setEsito(ESITO.KO);
  199.                     StringBuilder messageString = new StringBuilder();
  200.                     for(ValidationMessage msg: validate) {
  201.                         String errorMsg = msg.getCode() + " " + msg.getMessage();
  202.                         response.getErrors().add(errorMsg);
  203.                         messageString.append(errorMsg).append("\n");
  204.                     }
  205.                     if(this.logError) {
  206.                         ValidationUtils.logError(this.log, messageString.toString(), rawObject, this.schemaBytes, this.jsonSchema);
  207.                     }
  208.                     response.setException(new Exception(messageString.toString()));
  209.    
  210.                 }
  211.             }
  212.            
  213.         } catch(Exception e) {
  214.             throw new ValidationException(e);
  215.         }
  216.         return response;
  217.     }
  218.    
  219. }