From 0f687ae98fae9a39da53725fa5a81301ed58f5c4 Mon Sep 17 00:00:00 2001 From: "michele.artini" Date: Tue, 6 Dec 2022 10:16:49 +0100 Subject: [PATCH] xml validation --- .../is/importer/OldProfilesImporter.java | 6 + .../is/resources/SimpleResourceService.java | 19 ++- .../java/eu/dnetlib/is/util/JsonResource.java | 7 + .../eu/dnetlib/is/util/ResourceValidator.java | 107 +++++++++++++++ .../java/eu/dnetlib/is/util/XmlIndenter.java | 2 +- .../main/resources/schemas/cleaning_rule.xsd | 18 +++ .../schemas/hadoop_job_configuration.xsd | 122 ++++++++++++++++++ .../resources/static/js/simpleResources.js | 2 + .../resources/templates/simpleResources.html | 4 +- 9 files changed, 278 insertions(+), 9 deletions(-) create mode 100644 apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/JsonResource.java create mode 100644 apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/ResourceValidator.java create mode 100644 apps/dnet-is-application/src/main/resources/schemas/cleaning_rule.xsd create mode 100644 apps/dnet-is-application/src/main/resources/schemas/hadoop_job_configuration.xsd diff --git a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/importer/OldProfilesImporter.java b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/importer/OldProfilesImporter.java index 79e883b6..7bf6b1ef 100644 --- a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/importer/OldProfilesImporter.java +++ b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/importer/OldProfilesImporter.java @@ -16,6 +16,7 @@ import org.springframework.stereotype.Service; import eu.dnetlib.is.resource.model.SimpleResource; import eu.dnetlib.is.resource.repository.SimpleResourceRepository; import eu.dnetlib.is.util.InformationServiceException; +import eu.dnetlib.is.util.ResourceValidator; import eu.dnetlib.is.util.XmlIndenter; import eu.dnetlib.is.vocabulary.model.Synonym; import eu.dnetlib.is.vocabulary.model.Vocabulary; @@ -35,6 +36,9 @@ public class OldProfilesImporter { @Autowired private VocabularyTermRepository vocabularyTermRepository; + @Autowired + private ResourceValidator resourceValidator; + @Transactional public String importSimpleResource(final String xml) throws InformationServiceException { final SAXReader reader = new SAXReader(); @@ -92,6 +96,8 @@ public class OldProfilesImporter { throw new InformationServiceException("Invalid resource type: " + doc.valueOf("//RESOURCE_TYPE/@value")); } + resourceValidator.validate(res.getType(), resContent.trim()); + simpleResourceRepository.save(res); simpleResourceRepository.setContentById(id, resContent.trim()); diff --git a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/resources/SimpleResourceService.java b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/resources/SimpleResourceService.java index 87154c0d..3f3cc9d6 100644 --- a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/resources/SimpleResourceService.java +++ b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/resources/SimpleResourceService.java @@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.PathVariable; import eu.dnetlib.is.resource.model.SimpleResource; import eu.dnetlib.is.resource.repository.SimpleResourceRepository; import eu.dnetlib.is.util.InformationServiceException; +import eu.dnetlib.is.util.ResourceValidator; @Service public class SimpleResourceService { @@ -24,6 +25,9 @@ public class SimpleResourceService { @Autowired private SimpleResourceRepository simpleResourceRepository; + @Autowired + private ResourceValidator resourceValidator; + private static final Log log = LogFactory.getLog(SimpleResourceService.class); public SimpleResource getMetadata(final String id) throws InformationServiceException { @@ -54,7 +58,10 @@ public class SimpleResourceService { public SimpleResource saveNewResource(final String name, final String type, final String description, - final String content) { + final String content) throws InformationServiceException { + + resourceValidator.validate(type, content); + final Date now = new Date(); final SimpleResource res = new SimpleResource(); @@ -83,11 +90,11 @@ public class SimpleResourceService { @Transactional public void saveContent(final String id, final String content) throws InformationServiceException { - if (simpleResourceRepository.existsById(id)) { - simpleResourceRepository.setContentById(id, content); - } else { - throw new InformationServiceException("Resource not found"); - } + final SimpleResource res = simpleResourceRepository.findById(id).orElseThrow(() -> new InformationServiceException("Resource not found")); + + resourceValidator.validate(res.getType(), content); + + simpleResourceRepository.setContentById(id, content); } } diff --git a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/JsonResource.java b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/JsonResource.java new file mode 100644 index 00000000..328edbfe --- /dev/null +++ b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/JsonResource.java @@ -0,0 +1,7 @@ +package eu.dnetlib.is.util; + +public @interface JsonResource { + + String value(); + +} diff --git a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/ResourceValidator.java b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/ResourceValidator.java new file mode 100644 index 00000000..dd9560f2 --- /dev/null +++ b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/ResourceValidator.java @@ -0,0 +1,107 @@ +package eu.dnetlib.is.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; + +import javax.xml.XMLConstants; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.xml.sax.SAXException; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import eu.dnetlib.is.resource.model.ResourceType; +import eu.dnetlib.is.resource.repository.ResourceTypeRepository; + +@Component +public class ResourceValidator { + + private final Log log = LogFactory.getLog(ResourceValidator.class); + + @Autowired + private ResourceTypeRepository resourceTypeRepository; + + public void validate(final String type, final String content) throws InformationServiceException { + + final ResourceType rtype = resourceTypeRepository.findById(type).orElseThrow(() -> new InformationServiceException("Invalid type")); + + if (rtype.getContentType().equals(MediaType.APPLICATION_XML_VALUE)) { + validateXml(type, content); + } else if (rtype.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) { + validateJSON(type, content); + } + } + + private void validateXml(final String type, final String content) throws InformationServiceException { + final Schema schema = getXmlSchema(type); + if (schema == null) { + log.warn("no registered schema for " + type); + return; + } + final Validator validator = schema.newValidator(); + + try { + validator.validate(new StreamSource(new StringReader(content))); + } catch (final Exception e) { + throw new InformationServiceException("invalid resource", e); + } + } + + private Schema getXmlSchema(final String type) { + try { + final InputStream is = getClass().getResourceAsStream("/schemas/" + type + ".xsd"); + if (is == null) { return null; } + final String schemaSource = IOUtils.toString(is, StandardCharsets.UTF_8.name()); + if (StringUtils.isBlank(schemaSource)) { return null; } + return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new StreamSource(new StringReader(schemaSource))); + } catch (final SAXException | IOException e) { + log.fatal("cannot parse resource type schema", e); + return null; + } + } + + private void validateJSON(final String type, final String content) throws InformationServiceException { + final Class clazz = getJsonClass(type); + if (clazz == null) { + log.warn("no registered Class for " + type); + } + final ObjectMapper mapper = new ObjectMapper(); + + try { + mapper.readValue(content, clazz); + } catch (final Exception e) { + throw new InformationServiceException("invalid resource", e); + } + } + + private Class getJsonClass(final String type) { + final ClassLoader classLoader = ResourceValidator.class.getClassLoader(); + + final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true); + + provider.addIncludeFilter(new AnnotationTypeFilter(JsonResource.class)); + provider.setResourceLoader(new PathMatchingResourcePatternResolver(classLoader)); + + for (final BeanDefinition bd : provider.findCandidateComponents("eu.dnetlib")) { + if (bd.getClass().getAnnotation(JsonResource.class).value().equals(type)) { return bd.getClass(); } + } + return null; + } + +} diff --git a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/XmlIndenter.java b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/XmlIndenter.java index bf266c95..9b89cf7e 100644 --- a/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/XmlIndenter.java +++ b/apps/dnet-is-application/src/main/java/eu/dnetlib/is/util/XmlIndenter.java @@ -29,7 +29,7 @@ public class XmlIndenter { public static String indent(final Node node) { try { final StringWriter sw = new StringWriter(); - final XMLWriter writer = new XMLWriter(sw, new OutputFormat("\t", true)); + final XMLWriter writer = new XMLWriter(sw, OutputFormat.createPrettyPrint()); writer.write(node); writer.close(); return sw.toString(); diff --git a/apps/dnet-is-application/src/main/resources/schemas/cleaning_rule.xsd b/apps/dnet-is-application/src/main/resources/schemas/cleaning_rule.xsd new file mode 100644 index 00000000..ccd170cd --- /dev/null +++ b/apps/dnet-is-application/src/main/resources/schemas/cleaning_rule.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/dnet-is-application/src/main/resources/schemas/hadoop_job_configuration.xsd b/apps/dnet-is-application/src/main/resources/schemas/hadoop_job_configuration.xsd new file mode 100644 index 00000000..33e18e28 --- /dev/null +++ b/apps/dnet-is-application/src/main/resources/schemas/hadoop_job_configuration.xsd @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/dnet-is-application/src/main/resources/static/js/simpleResources.js b/apps/dnet-is-application/src/main/resources/static/js/simpleResources.js index 0b5c39cc..89c5b1e5 100644 --- a/apps/dnet-is-application/src/main/resources/static/js/simpleResources.js +++ b/apps/dnet-is-application/src/main/resources/static/js/simpleResources.js @@ -49,6 +49,7 @@ app.controller('resourcesController', function($scope, $http) { 'content' : r.content })).then(function successCallback(res) { alert("Resource saved"); + $('#newResourceModal').modal('hide'); $scope.reload(); }, function errorCallback(res) { alert('ERROR: ' + res.data.message); @@ -71,6 +72,7 @@ app.controller('resourcesController', function($scope, $http) { 'content' : content })).then(function successCallback(res) { alert("Resource saved"); + $('#editContentModal').modal('hide'); }, function errorCallback(res) { alert('ERROR: ' + res.data.message); }); diff --git a/apps/dnet-is-application/src/main/resources/templates/simpleResources.html b/apps/dnet-is-application/src/main/resources/templates/simpleResources.html index 8a2b874c..672eaefe 100644 --- a/apps/dnet-is-application/src/main/resources/templates/simpleResources.html +++ b/apps/dnet-is-application/src/main/resources/templates/simpleResources.html @@ -108,7 +108,7 @@ @@ -131,7 +131,7 @@