forked from liquibase/liquigraph
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[liquibase#71] Implement validation against explicit schema
This change is backward compatible: users can still define their changelog files without explicit schema, it will work as expected. This commit introduces a schema detector, from which the proper validation strategy is deduced.
- Loading branch information
Florent Biville
committed
Apr 22, 2016
1 parent
19b8a3c
commit 588ea53
Showing
11 changed files
with
446 additions
and
31 deletions.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
liquigraph-core/src/main/java/org/liquigraph/core/io/xml/DomSourceValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* Copyright 2014-2016 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.liquigraph.core.io.xml; | ||
|
||
import javax.xml.transform.dom.DOMSource; | ||
import java.util.Collection; | ||
|
||
interface DomSourceValidator { | ||
|
||
Collection<String> validate(DOMSource source) throws Exception; | ||
} |
34 changes: 34 additions & 0 deletions
34
liquigraph-core/src/main/java/org/liquigraph/core/io/xml/DomSourceValidatorFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* Copyright 2014-2016 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.liquigraph.core.io.xml; | ||
|
||
import javax.xml.transform.dom.DOMSource; | ||
|
||
public class DomSourceValidatorFactory { | ||
|
||
private final SchemaDetector schemaDetector; | ||
|
||
public DomSourceValidatorFactory() { | ||
schemaDetector = new SchemaDetector(); | ||
} | ||
|
||
public DomSourceValidator createValidator(DOMSource source) { | ||
if (schemaDetector.hasExplicitSchema(source)) { | ||
return new ExplicitSchemaValidator(); | ||
} | ||
return new ImplicitSchemaValidator(); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
liquigraph-core/src/main/java/org/liquigraph/core/io/xml/ExplicitSchemaValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/** | ||
* Copyright 2014-2016 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.liquigraph.core.io.xml; | ||
|
||
import org.xml.sax.InputSource; | ||
import org.xml.sax.SAXException; | ||
import org.xml.sax.SAXNotRecognizedException; | ||
import org.xml.sax.SAXNotSupportedException; | ||
import org.xml.sax.XMLReader; | ||
|
||
import javax.xml.parsers.ParserConfigurationException; | ||
import javax.xml.parsers.SAXParser; | ||
import javax.xml.parsers.SAXParserFactory; | ||
import javax.xml.transform.TransformerException; | ||
import javax.xml.transform.TransformerFactory; | ||
import javax.xml.transform.dom.DOMSource; | ||
import javax.xml.transform.stream.StreamResult; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.StringWriter; | ||
import java.util.Collection; | ||
|
||
class ExplicitSchemaValidator implements DomSourceValidator { | ||
|
||
private final SAXParserFactory saxParserFactory; | ||
|
||
public ExplicitSchemaValidator() { | ||
saxParserFactory = SAXParserFactory.newInstance(); | ||
saxParserFactory.setValidating(true); | ||
saxParserFactory.setNamespaceAware(true); | ||
} | ||
|
||
@Override | ||
public Collection<String> validate(DOMSource changelog) throws Exception { | ||
SchemaErrorHandler errorHandler = new SchemaErrorHandler(); | ||
XMLReader reader = saxParser().getXMLReader(); | ||
reader.setErrorHandler(errorHandler); | ||
parse(changelog, reader); | ||
return errorHandler.getErrors(); | ||
} | ||
|
||
private SAXParser saxParser() throws ParserConfigurationException, SAXException { | ||
SAXParser saxParser = saxParserFactory.newSAXParser(); | ||
try { | ||
saxParser.setProperty( | ||
"http://java.sun.com/xml/jaxp/properties/schemaLanguage", | ||
"http://www.w3.org/2001/XMLSchema" | ||
); | ||
} catch (SAXNotRecognizedException | SAXNotSupportedException ignored) { | ||
} | ||
return saxParser; | ||
} | ||
|
||
// quick'n'dirty: this is super inefficient | ||
private void parse(DOMSource changelog, XMLReader reader) throws IOException, TransformerException, SAXException { | ||
try (StringWriter writer = new StringWriter()) { | ||
StreamResult result = new StreamResult(writer); | ||
TransformerFactory.newInstance().newTransformer().transform(changelog, result); | ||
try (InputStream inputStream = new ByteArrayInputStream(writer.toString().getBytes("UTF-8"))) { | ||
reader.parse(new InputSource(inputStream)); | ||
} | ||
} | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
liquigraph-core/src/main/java/org/liquigraph/core/io/xml/ImplicitSchemaValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** | ||
* Copyright 2014-2016 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.liquigraph.core.io.xml; | ||
|
||
import org.xml.sax.SAXException; | ||
|
||
import javax.xml.XMLConstants; | ||
import javax.xml.transform.dom.DOMSource; | ||
import javax.xml.transform.stream.StreamSource; | ||
import javax.xml.validation.Schema; | ||
import javax.xml.validation.SchemaFactory; | ||
import javax.xml.validation.Validator; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.Collection; | ||
|
||
class ImplicitSchemaValidator implements DomSourceValidator { | ||
|
||
private final Schema schema; | ||
|
||
public ImplicitSchemaValidator() { | ||
schema = implicitSchema("/schema/changelog.xsd"); | ||
} | ||
|
||
@Override | ||
public Collection<String> validate(DOMSource source) throws Exception { | ||
SchemaErrorHandler errorHandler = new SchemaErrorHandler(); | ||
validator(errorHandler).validate(source); | ||
return errorHandler.getErrors(); | ||
} | ||
|
||
private static Schema implicitSchema(String name) { | ||
try (InputStream stream = XmlSchemaValidator.class.getResourceAsStream(name)) { | ||
return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new StreamSource(stream)); | ||
} catch (SAXException | IOException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
|
||
private Validator validator(SchemaErrorHandler customErrorHandler) { | ||
Validator validator = schema.newValidator(); | ||
validator.setErrorHandler(customErrorHandler); | ||
return validator; | ||
} | ||
|
||
} |
71 changes: 71 additions & 0 deletions
71
liquigraph-core/src/main/java/org/liquigraph/core/io/xml/SchemaDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/** | ||
* Copyright 2014-2016 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.liquigraph.core.io.xml; | ||
|
||
import org.w3c.dom.NamedNodeMap; | ||
import org.w3c.dom.Node; | ||
|
||
import javax.xml.transform.dom.DOMSource; | ||
import java.util.Iterator; | ||
|
||
class SchemaDetector { | ||
|
||
public boolean hasExplicitSchema(DOMSource changelog) { | ||
Node node = changelog.getNode(); | ||
if (node.getNodeType() == Node.DOCUMENT_NODE) { | ||
return hasExplicitSchema(rootElement(node)); | ||
} | ||
return hasExplicitSchema(node); | ||
} | ||
|
||
private Node rootElement(Node document) { | ||
Iterator<Node> iterator = new NodeListIterator(document.getChildNodes()); | ||
while (iterator.hasNext()) { | ||
Node node = iterator.next(); | ||
if (node.getNodeType() == Node.ELEMENT_NODE | ||
&& node.getNodeName().equals("changelog")) { | ||
return node; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private boolean hasExplicitSchema(Node node) { | ||
if (node == null) { | ||
return false; | ||
} | ||
if (node.getNodeType() != Node.ELEMENT_NODE) { | ||
return false; | ||
} | ||
|
||
NamedNodeMap attributes = node.getAttributes(); | ||
if (attributes == null) { | ||
return false; | ||
} | ||
return findAttributeByName(attributes, "xsi:noNamespaceSchemaLocation"); | ||
} | ||
|
||
private boolean findAttributeByName(NamedNodeMap attributes, String name) { | ||
int i = 0; | ||
while (i < attributes.getLength()) { | ||
if (name.equals(attributes.item(i).getNodeName())) { | ||
return true; | ||
} | ||
i++; | ||
} | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.