-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
XMLChangeLogSAXParser.java
154 lines (135 loc) · 6.46 KB
/
XMLChangeLogSAXParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package liquibase.parser.core.xml;
import liquibase.GlobalConfiguration;
import liquibase.Scope;
import liquibase.changelog.ChangeLogParameters;
import liquibase.exception.ChangeLogParseException;
import liquibase.parser.core.ParsedNode;
import liquibase.resource.ResourceAccessor;
import liquibase.util.BomAwareInputStream;
import liquibase.util.FileUtil;
import org.xml.sax.*;
import javax.xml.XMLConstants;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
public class XMLChangeLogSAXParser extends AbstractChangeLogParser {
public static final String LIQUIBASE_SCHEMA_VERSION = "4.6";
private SAXParserFactory saxParserFactory;
private final LiquibaseEntityResolver resolver = new LiquibaseEntityResolver();
public XMLChangeLogSAXParser() {
saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setValidating(true);
saxParserFactory.setNamespaceAware(true);
if (GlobalConfiguration.SECURE_PARSING.getCurrentValue()) {
try {
saxParserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (Throwable e) {
Scope.getCurrentScope().getLog(getClass()).fine("Cannot enable FEATURE_SECURE_PROCESSING: " + e.getMessage(), e);
}
}
}
@Override
public int getPriority() {
return PRIORITY_DEFAULT;
}
public static String getSchemaVersion() {
return LIQUIBASE_SCHEMA_VERSION;
}
@Override
public boolean supports(String changeLogFile, ResourceAccessor resourceAccessor) {
return changeLogFile.toLowerCase().endsWith("xml");
}
protected SAXParserFactory getSaxParserFactory() {
return saxParserFactory;
}
@Override
protected ParsedNode parseToNode(String physicalChangeLogLocation, ChangeLogParameters changeLogParameters, ResourceAccessor resourceAccessor) throws ChangeLogParseException {
try (InputStream inputStream = resourceAccessor.openStream(null, physicalChangeLogLocation)) {
SAXParser parser = saxParserFactory.newSAXParser();
if (GlobalConfiguration.SECURE_PARSING.getCurrentValue()) {
try {
parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "http,https"); //need to allow external schemas on http/https to support the liquibase.org xsd files
} catch (SAXException e) {
Scope.getCurrentScope().getLog(getClass()).fine("Cannot enable ACCESS_EXTERNAL_SCHEMA: " + e.getMessage(), e);
}
}
trySetSchemaLanguageProperty(parser);
XMLReader xmlReader = parser.getXMLReader();
xmlReader.setEntityResolver(resolver);
xmlReader.setErrorHandler(new ErrorHandler() {
@Override
public void warning(SAXParseException exception) throws SAXException {
Scope.getCurrentScope().getLog(getClass()).warning(exception.getMessage());
throw exception;
}
@Override
public void error(SAXParseException exception) throws SAXException {
Scope.getCurrentScope().getLog(getClass()).severe(exception.getMessage());
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
Scope.getCurrentScope().getLog(getClass()).severe(exception.getMessage());
throw exception;
}
});
if (inputStream == null) {
if (physicalChangeLogLocation.startsWith("WEB-INF/classes/")) {
// Correct physicalChangeLogLocation and try again.
return parseToNode(
physicalChangeLogLocation.replaceFirst("WEB-INF/classes/", ""),
changeLogParameters, resourceAccessor);
} else {
throw new ChangeLogParseException(FileUtil.getFileNotFoundMessage(physicalChangeLogLocation));
}
}
XMLChangeLogSAXHandler contentHandler = new XMLChangeLogSAXHandler(physicalChangeLogLocation, resourceAccessor, changeLogParameters);
xmlReader.setContentHandler(contentHandler);
xmlReader.parse(new InputSource(new BomAwareInputStream(inputStream)));
return contentHandler.getDatabaseChangeLogTree();
} catch (ChangeLogParseException e) {
throw e;
} catch (IOException e) {
throw new ChangeLogParseException("Error Reading Changelog File: " + e.getMessage(), e);
} catch (SAXParseException e) {
throw new ChangeLogParseException("Error parsing line " + e.getLineNumber() + " column " + e.getColumnNumber() + " of " + physicalChangeLogLocation + ": " + e.getMessage(), e);
} catch (SAXException e) {
Throwable parentCause = e.getException();
while (parentCause != null) {
if (parentCause instanceof ChangeLogParseException) {
throw ((ChangeLogParseException) parentCause);
}
parentCause = parentCause.getCause();
}
String reason = e.getMessage();
String causeReason = null;
if (e.getCause() != null) {
causeReason = e.getCause().getMessage();
}
if (reason == null) {
if (causeReason != null) {
reason = causeReason;
} else {
reason = "Unknown Reason";
}
}
throw new ChangeLogParseException("Invalid Migration File: " + reason, e);
} catch (Exception e) {
throw new ChangeLogParseException(e);
}
}
/**
* Try to set the parser property "schemaLanguage", but do not mind if the parser does not understand it.
*
* @param parser the parser to configure
* @todo If we do not mind, why do we set it in the first place? Need to resarch in git...
*/
private void trySetSchemaLanguageProperty(SAXParser parser) {
try {
parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
} catch (SAXNotRecognizedException | SAXNotSupportedException ignored) {
//ok, parser need not support it
}
}
}