[#6260] Support loading multiple files in XMLDatabase
This commit is contained in:
parent
89f79d5f47
commit
df54d660f4
@ -42,13 +42,10 @@ import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.tools.StringUtils.isBlank;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -69,8 +66,9 @@ import org.jooq.impl.DSL;
|
||||
import org.jooq.impl.DefaultVisitListener;
|
||||
import org.jooq.impl.ParserException;
|
||||
import org.jooq.meta.SchemaDefinition;
|
||||
import org.jooq.meta.extensions.tools.FileComparator;
|
||||
import org.jooq.meta.h2.H2Database;
|
||||
import org.jooq.meta.tools.FilePattern;
|
||||
import org.jooq.meta.tools.FilePattern.Loader;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.jdbc.JDBCUtils;
|
||||
|
||||
@ -95,7 +93,6 @@ public class DDLDatabase extends H2Database {
|
||||
|
||||
private Connection connection;
|
||||
private DSLContext ctx;
|
||||
private Comparator<File> fileComparator;
|
||||
private boolean publicIsDefault;
|
||||
|
||||
@Override
|
||||
@ -108,18 +105,7 @@ public class DDLDatabase extends H2Database {
|
||||
String defaultNameCase = getProperties().getProperty("defaultNameCase", "as_is").toUpperCase();
|
||||
|
||||
publicIsDefault = "none".equals(unqualifiedSchema);
|
||||
|
||||
if ("alphanumeric".equals(sort))
|
||||
fileComparator = new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File o1, File o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
};
|
||||
else if ("none".equals(sort))
|
||||
fileComparator = null;
|
||||
else
|
||||
fileComparator = FileComparator.INSTANCE;
|
||||
Comparator<File> fileComparator = FilePattern.fileComparator(sort);
|
||||
|
||||
if (isBlank(scripts)) {
|
||||
scripts = "";
|
||||
@ -162,41 +148,12 @@ public class DDLDatabase extends H2Database {
|
||||
});
|
||||
}
|
||||
|
||||
InputStream in = null;
|
||||
boolean loaded = false;
|
||||
in = DDLDatabase.class.getResourceAsStream(scripts);
|
||||
if (in != null) {
|
||||
log.info("Reading from classpath: " + scripts);
|
||||
load(encoding, in);
|
||||
loaded = true;
|
||||
}
|
||||
else {
|
||||
File file = new File(scripts);
|
||||
|
||||
if (file.exists()) {
|
||||
load(encoding, file, null);
|
||||
loaded = true;
|
||||
FilePattern.load(encoding, scripts, fileComparator, new Loader() {
|
||||
@Override
|
||||
public void load(String e, InputStream in) {
|
||||
DDLDatabase.this.load(e, in);
|
||||
}
|
||||
else if (scripts.contains("*") || scripts.contains("?")) {
|
||||
file = new File(scripts.replaceAll("[*?].*", ""));
|
||||
|
||||
Pattern pattern = Pattern.compile("^.*?"
|
||||
+ scripts
|
||||
.replace("\\", "/")
|
||||
.replace(".", "\\.")
|
||||
.replace("?", ".")
|
||||
.replace("**", ".+?")
|
||||
.replace("*", "[^/]*")
|
||||
+ "$"
|
||||
);
|
||||
|
||||
load(encoding, file, pattern);
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
log.error("Could not find script source : " + scripts);
|
||||
});
|
||||
}
|
||||
catch (ParserException e) {
|
||||
log.error("An exception occurred while parsing script source : " + scripts + ". Please report this error to https://github.com/jOOQ/jOOQ/issues/new", e);
|
||||
@ -210,33 +167,6 @@ public class DDLDatabase extends H2Database {
|
||||
return DSL.using(connection);
|
||||
}
|
||||
|
||||
private void load(String encoding, File file, Pattern pattern) throws IOException {
|
||||
if (file.isFile()) {
|
||||
if (pattern == null || pattern.matcher(file.getCanonicalPath().replace("\\", "/")).matches()) {
|
||||
log.info("Reading from: " + file + " [*]");
|
||||
load(encoding, new FileInputStream(file));
|
||||
}
|
||||
}
|
||||
else if (file.isDirectory()) {
|
||||
log.info("Reading from: " + file);
|
||||
|
||||
File[] files = file.listFiles();
|
||||
|
||||
if (files != null) {
|
||||
if (fileComparator != null)
|
||||
Arrays.sort(files, fileComparator);
|
||||
|
||||
for (File f : files)
|
||||
load(encoding, f, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
// [#7767] Backtrack to a parent directory in case the current file pattern doesn't match yet
|
||||
else if (!file.exists() && file.getParentFile() != null) {
|
||||
load(encoding, file.getParentFile(), pattern);
|
||||
}
|
||||
}
|
||||
|
||||
private void load(String encoding, InputStream in) {
|
||||
try {
|
||||
Scanner s = new Scanner(in, encoding).useDelimiter("\\A");
|
||||
|
||||
@ -48,5 +48,14 @@
|
||||
<groupId>org.jooq</groupId>
|
||||
<artifactId>jooq</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.meta.extensions.tools;
|
||||
package org.jooq.meta.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Comparator;
|
||||
181
jOOQ-meta/src/main/java/org/jooq/meta/tools/FilePattern.java
Normal file
181
jOOQ-meta/src/main/java/org/jooq/meta/tools/FilePattern.java
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.meta.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jooq.tools.JooqLogger;
|
||||
|
||||
/**
|
||||
* A utility class that can traverse a directory structure given some ant-style
|
||||
* file patterns.
|
||||
* <p>
|
||||
* This is INTERNAL API. Please do not use directly as API may change
|
||||
* incompatibly.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public final class FilePattern {
|
||||
|
||||
private static final JooqLogger log = JooqLogger.getLogger(FilePattern.class);
|
||||
|
||||
public static final Comparator<File> fileComparator(String sort) {
|
||||
if ("alphanumeric".equals(sort))
|
||||
return new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File o1, File o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
};
|
||||
else if ("none".equals(sort))
|
||||
return null;
|
||||
else
|
||||
return FileComparator.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try loading <code>pattern</code>.
|
||||
* <p>
|
||||
* This method tries loading contents from <code>pattern</code> using the
|
||||
* following algorithm:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>If <code>pattern</code> is a valid classpath resource, load it from
|
||||
* there</li>
|
||||
* <li>If <code>pattern</code> is a valid file on the file system, load it
|
||||
* from there</li>
|
||||
* <li>Match all files on the file system according to <code>pattern</code>
|
||||
* (interpreted as an ant-style file pattern), and load them</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final void load(
|
||||
String encoding,
|
||||
String pattern,
|
||||
Comparator<File> fileComparator,
|
||||
Loader loader
|
||||
) throws Exception {
|
||||
InputStream in = null;
|
||||
boolean loaded = false;
|
||||
|
||||
try {
|
||||
in = FilePattern.class.getResourceAsStream(pattern);
|
||||
|
||||
if (in != null) {
|
||||
log.info("Reading from classpath: " + pattern);
|
||||
loader.load(encoding, in);
|
||||
loaded = true;
|
||||
}
|
||||
else {
|
||||
File file = new File(pattern);
|
||||
|
||||
if (file.exists()) {
|
||||
load(encoding, file, fileComparator, null, loader);
|
||||
loaded = true;
|
||||
}
|
||||
else if (pattern.contains("*") || pattern.contains("?")) {
|
||||
file = new File(pattern.replaceAll("[*?].*", "")).getCanonicalFile();
|
||||
|
||||
Pattern regex = Pattern.compile("^.*?"
|
||||
+ pattern
|
||||
.replace("\\", "/")
|
||||
.replace(".", "\\.")
|
||||
.replace("?", ".")
|
||||
.replace("**", ".+?")
|
||||
.replace("*", "[^/]*")
|
||||
+ "$"
|
||||
);
|
||||
|
||||
load(encoding, file, fileComparator, regex, loader);
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
log.error("Could not find source(s) : " + pattern);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (in != null)
|
||||
in.close();
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
private static final void load(
|
||||
String encoding,
|
||||
File file,
|
||||
Comparator<File> fileComparator,
|
||||
Pattern pattern,
|
||||
Loader loader
|
||||
) throws Exception {
|
||||
if (file.isFile()) {
|
||||
if (pattern == null || pattern.matcher(file.getCanonicalPath().replace("\\", "/")).matches()) {
|
||||
log.info("Reading from: " + file + " [*]");
|
||||
loader.load(encoding, new FileInputStream(file));
|
||||
}
|
||||
}
|
||||
else if (file.isDirectory()) {
|
||||
log.info("Reading from: " + file);
|
||||
|
||||
File[] files = file.listFiles();
|
||||
|
||||
if (files != null) {
|
||||
if (fileComparator != null)
|
||||
Arrays.sort(files, fileComparator);
|
||||
|
||||
for (File f : files)
|
||||
load(encoding, f, fileComparator, pattern, loader);
|
||||
}
|
||||
}
|
||||
|
||||
// [#7767] Backtrack to a parent directory in case the current file pattern doesn't match yet
|
||||
else if (!file.exists() && file.getParentFile() != null) {
|
||||
load(encoding, file.getParentFile(), fileComparator, pattern, loader);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Loader {
|
||||
void load(String encoding, InputStream in) throws Exception;
|
||||
}
|
||||
}
|
||||
@ -35,7 +35,7 @@
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.meta.extensions.tools;
|
||||
package org.jooq.meta.tools;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Comparator;
|
||||
@ -45,14 +45,13 @@ import static org.jooq.tools.StringUtils.isBlank;
|
||||
import static org.jooq.util.xml.jaxb.TableConstraintType.PRIMARY_KEY;
|
||||
import static org.jooq.util.xml.jaxb.TableConstraintType.UNIQUE;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -103,6 +102,8 @@ import org.jooq.meta.SchemaDefinition;
|
||||
import org.jooq.meta.SequenceDefinition;
|
||||
import org.jooq.meta.TableDefinition;
|
||||
import org.jooq.meta.UDTDefinition;
|
||||
import org.jooq.meta.tools.FilePattern;
|
||||
import org.jooq.meta.tools.FilePattern.Loader;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.StringUtils;
|
||||
import org.jooq.util.xml.jaxb.Index;
|
||||
@ -132,122 +133,115 @@ public class XMLDatabase extends AbstractDatabase {
|
||||
if (info == null) {
|
||||
|
||||
// [#8115] Support old property name style for backwards compatibility reasons
|
||||
String xml = getProperties().getProperty("xmlFile", getProperties().getProperty("xml-file"));
|
||||
String xsl = getProperties().getProperty("xslFile", getProperties().getProperty("xsl-file"));
|
||||
final String xml = getProperties().getProperty("xmlFiles",
|
||||
getProperties().getProperty("xmlFile",
|
||||
getProperties().getProperty("xml-file")
|
||||
)
|
||||
);
|
||||
final String xsl = getProperties().getProperty("xslFile",
|
||||
getProperties().getProperty("xsl-file")
|
||||
);
|
||||
final String sort = getProperties().getProperty("sort", "semantic").toLowerCase();
|
||||
|
||||
if (xml == null)
|
||||
throw new RuntimeException("Must provide an xmlFile property");
|
||||
|
||||
log.info("Using XML file", xml);
|
||||
|
||||
try {
|
||||
String content;
|
||||
FilePattern.load("UTF-8", xml, FilePattern.fileComparator(sort), new Loader() {
|
||||
@Override
|
||||
public void load(String enc, InputStream in) throws Exception {
|
||||
String content;
|
||||
|
||||
if (StringUtils.isBlank(xsl)) {
|
||||
RandomAccessFile f = null;
|
||||
if (StringUtils.isBlank(xsl)) {
|
||||
byte[] bytes = bytes(in);
|
||||
|
||||
try {
|
||||
URL url = XMLDatabase.class.getResource(xml);
|
||||
File file = url != null ? new File(url.toURI()) : new File(xml);
|
||||
// [#7414] Default to reading UTF-8
|
||||
content = new String(bytes, "UTF-8");
|
||||
|
||||
f = new RandomAccessFile(file, "r");
|
||||
byte[] bytes = new byte[(int) f.length()];
|
||||
f.readFully(bytes);
|
||||
// [#7414] Alternatively, read the encoding from the XML file
|
||||
try {
|
||||
XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(content));
|
||||
String encoding = reader.getCharacterEncodingScheme();
|
||||
|
||||
// [#7414] Default to reading UTF-8
|
||||
content = new String(bytes, "UTF-8");
|
||||
// Returned encoding can be null in the presence of a BOM
|
||||
// See https://stackoverflow.com/a/27147259/521799
|
||||
if (encoding != null && !"UTF-8".equals(encoding))
|
||||
content = new String(bytes, encoding);
|
||||
}
|
||||
catch (XMLStreamException e) {
|
||||
log.warn("Could not open XML Stream: " + e.getMessage());
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
log.warn("Unsupported encoding: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
InputStream xslIs = null;
|
||||
|
||||
try {
|
||||
log.info("Using XSL file", xsl);
|
||||
|
||||
xslIs = XMLDatabase.class.getResourceAsStream(xsl);
|
||||
if (xslIs == null)
|
||||
xslIs = new FileInputStream(xsl);
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
TransformerFactory factory = TransformerFactory.newInstance();
|
||||
Transformer transformer = factory.newTransformer(new StreamSource(xslIs));
|
||||
|
||||
transformer.transform(new StreamSource(in), new StreamResult(writer));
|
||||
content = writer.getBuffer().toString();
|
||||
}
|
||||
catch (TransformerException e) {
|
||||
throw new RuntimeException("Error while transforming XML file " + xml + " with XSL file " + xsl, e);
|
||||
}
|
||||
finally {
|
||||
if (xslIs != null) {
|
||||
try {
|
||||
xslIs.close();
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [#1201] Add better error handling here
|
||||
content = content.replaceAll(
|
||||
"<(\\w+:)?information_schema xmlns(:\\w+)?=\"http://www.jooq.org/xsd/jooq-meta-\\d+\\.\\d+\\.\\d+.xsd\">",
|
||||
"<$1information_schema xmlns$2=\"" + Constants.NS_META + "\">");
|
||||
|
||||
content = content.replace(
|
||||
"<information_schema>",
|
||||
"<information_schema xmlns=\"" + Constants.NS_META + "\">");
|
||||
|
||||
// [#7579] [#8044] Workaround for obscure JAXB bug on JDK 9+
|
||||
content = MiniJAXB.jaxbNamespaceBugWorkaround(content, new InformationSchema());
|
||||
|
||||
// [#7414] Alternatively, read the encoding from the XML file
|
||||
try {
|
||||
XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader( new StringReader(content) );
|
||||
String encoding = reader.getCharacterEncodingScheme();
|
||||
info = MiniJAXB.append(info, JAXB.unmarshal(new StringReader(content), InformationSchema.class));
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (ExceptionTools.getCause(t, ClassNotFoundException.class) != null ||
|
||||
ExceptionTools.getCause(t, Error.class) != null) {
|
||||
|
||||
// Returned encoding can be null in the presence of a BOM
|
||||
// See https://stackoverflow.com/a/27147259/521799
|
||||
if (encoding != null && !"UTF-8".equals(encoding))
|
||||
content = new String(bytes, encoding);
|
||||
}
|
||||
catch (XMLStreamException e) {
|
||||
log.warn("Could not open XML Stream: " + e.getMessage());
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
log.warn("Unsupported encoding: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (f != null) {
|
||||
try {
|
||||
f.close();
|
||||
info = MiniJAXB.append(info, MiniJAXB.unmarshal(content, InformationSchema.class));
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
else
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
InputStream xmlIs = null;
|
||||
InputStream xslIs = null;
|
||||
|
||||
try {
|
||||
log.info("Using XSL file", xsl);
|
||||
private byte[] bytes(InputStream in) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int read;
|
||||
byte[] buffer = new byte[16384];
|
||||
|
||||
xmlIs = XMLDatabase.class.getResourceAsStream(xml);
|
||||
if (xmlIs == null)
|
||||
xmlIs = new FileInputStream(xml);
|
||||
while ((read = in.read(buffer, 0, buffer.length)) != -1)
|
||||
bos.write(buffer, 0, read);
|
||||
|
||||
xslIs = XMLDatabase.class.getResourceAsStream(xsl);
|
||||
if (xslIs == null)
|
||||
xslIs = new FileInputStream(xsl);
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
TransformerFactory factory = TransformerFactory.newInstance();
|
||||
Transformer transformer = factory.newTransformer(new StreamSource(xslIs));
|
||||
|
||||
transformer.transform(new StreamSource(xmlIs), new StreamResult(writer));
|
||||
content = writer.getBuffer().toString();
|
||||
return bos.toByteArray();
|
||||
}
|
||||
catch (TransformerException e) {
|
||||
throw new RuntimeException("Error while transforming XML file " + xml + " with XSL file " + xsl, e);
|
||||
}
|
||||
finally {
|
||||
if (xmlIs != null) {
|
||||
try {
|
||||
xmlIs.close();
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
if (xslIs != null) {
|
||||
try {
|
||||
xslIs.close();
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [#1201] Add better error handling here
|
||||
content = content.replaceAll(
|
||||
"<(\\w+:)?information_schema xmlns(:\\w+)?=\"http://www.jooq.org/xsd/jooq-meta-\\d+\\.\\d+\\.\\d+.xsd\">",
|
||||
"<$1information_schema xmlns$2=\"" + Constants.NS_META + "\">");
|
||||
|
||||
content = content.replace(
|
||||
"<information_schema>",
|
||||
"<information_schema xmlns=\"" + Constants.NS_META + "\">");
|
||||
|
||||
// [#7579] [#8044] Workaround for obscure JAXB bug on JDK 9+
|
||||
content = MiniJAXB.jaxbNamespaceBugWorkaround(content, new InformationSchema());
|
||||
|
||||
try {
|
||||
info = JAXB.unmarshal(new StringReader(content), InformationSchema.class);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (ExceptionTools.getCause(t, ClassNotFoundException.class) != null ||
|
||||
ExceptionTools.getCause(t, Error.class) != null) {
|
||||
|
||||
info = MiniJAXB.unmarshal(content, InformationSchema.class);
|
||||
}
|
||||
else
|
||||
throw t;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Error while opening files " + xml + " or " + xsl, e);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user