1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.openehealth.ipf.commons.xml;
17
18 import org.openehealth.ipf.commons.core.modules.api.ValidationException;
19 import org.openehealth.ipf.commons.core.modules.api.Validator;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22 import org.xml.sax.ErrorHandler;
23 import org.xml.sax.SAXException;
24 import org.xml.sax.SAXParseException;
25
26 import javax.xml.XMLConstants;
27 import javax.xml.transform.Source;
28 import javax.xml.validation.Schema;
29 import javax.xml.validation.SchemaFactory;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.concurrent.ConcurrentMap;
35
36
37
38
39
40
41
42 public class XsdValidator extends AbstractCachingXmlProcessor<Schema> implements Validator<Source, String> {
43 private static final Logger LOG = LoggerFactory.getLogger(XsdValidator.class);
44
45 private static final ConcurrentMap<String, Loader<Schema>> XSD_CACHE = new ConcurrentHashMap<>();
46 private static final LSResourceResolverImpl RESOURCE_RESOLVER = new LSResourceResolverImpl();
47
48 private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
49
50 public XsdValidator() {
51 super(null);
52 }
53
54 public XsdValidator(ClassLoader classloader) {
55 super(classloader);
56 }
57
58 @Override
59 protected ConcurrentMap<String, Loader<Schema>> getCache() {
60 return XSD_CACHE;
61 }
62
63 @Override
64 public void validate(Source message, String schema) {
65 List<ValidationException> exceptions = doValidate(message, schema);
66 if (! exceptions.isEmpty()) {
67 throw new ValidationException(exceptions);
68 }
69 }
70
71
72
73
74
75
76
77
78 protected List<ValidationException> doValidate(Source message, String schemaResource) {
79 try {
80 LOG.debug("Validating XML message");
81 Schema schema = resource(schemaResource);
82 javax.xml.validation.Validator validator = schema.newValidator();
83 CollectingErrorHandler errorHandler = new CollectingErrorHandler();
84 validator.setErrorHandler(errorHandler);
85 validator.validate(message);
86 List<ValidationException> exceptions = errorHandler.getExceptions();
87 if (! exceptions.isEmpty()) {
88 LOG.debug("Message validation found {} problems", exceptions.size());
89 } else {
90 LOG.debug("Message validation successful");
91 }
92 return exceptions;
93 } catch (Exception e) {
94 return Collections.singletonList(new ValidationException(
95 "Unexpected validation failure because " + e.getMessage(), e));
96 }
97 }
98
99 @Override
100 protected Schema createResource(Object... params) {
101
102 SchemaFactory factory = SchemaFactory.newInstance(getSchemaLanguage());
103
104
105 factory.setResourceResolver(RESOURCE_RESOLVER);
106 try {
107 return factory.newSchema(resourceContent(params));
108 } catch (SAXException e) {
109 throw new IllegalArgumentException("Could not initialize XSD schema", e);
110 }
111 }
112
113 public String getSchemaLanguage() {
114 return schemaLanguage;
115 }
116
117 public void setSchemaLanguage(String schemaLanguage) {
118 this.schemaLanguage = schemaLanguage;
119 }
120
121
122
123
124
125
126
127 private static class CollectingErrorHandler implements ErrorHandler {
128
129 private final List<ValidationException> exceptions = new ArrayList<>();
130
131 @Override
132 public void error(SAXParseException exception) throws SAXException {
133 add(exception);
134 }
135
136 @Override
137 public void fatalError(SAXParseException exception) throws SAXException {
138 add(exception);
139 }
140
141 @Override
142 public void warning(SAXParseException exception) throws SAXException {
143
144 }
145
146 private void add(SAXParseException exception) {
147 exceptions.add(new ValidationException(exception));
148 }
149
150 public List<ValidationException> getExceptions() {
151 return exceptions;
152 }
153 }
154 }