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 @@
+