Java tutorial
/* * Copyright (c) 2008-2011 Simon Ritchie. * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see http://www.gnu.org/licenses/>. */ package org.rimudb.configuration; import java.io.*; import java.net.*; import javax.xml.*; import javax.xml.parsers.*; import javax.xml.transform.dom.*; import javax.xml.validation.*; import org.apache.commons.logging.*; import org.rimudb.xml.*; import org.w3c.dom.*; import org.xml.sax.*; /** * This abstract class performs the load an XML document from either the classpath, * file system or a URL. * * It can also perform validation of the document against an array of schemas. It * first attempts to validate using the name of the schema referenced in the document, * but will fall back to trying to validate against the schemas returned by the * abstract method getSchemas(). * * The load() method is responsible for loading the file. Given a filename, * it attempts to locate that file in several different ways. If the filename * begins with 'classpath:' it will attempt to load the filename as a resource. If the * filename begins with 'file:' or 'http:', it will attempt to load the file name as * a URL. If the filename begins with none of these, it will attempt to load it from * the local file system. If that fails, it will attempt to load it as a resource * from the classpath. * * @author Simon Ritchie * */ public abstract class AbstractXmlLoader { private static Log log = LogFactory.getLog(AbstractXmlLoader.class); private static final String SOURCE_CLASSPATH = "classpath:"; private String filename = null; private boolean validateXML = false; private String documentSchema = null; public AbstractXmlLoader() { } /** * Return an array of schemas to validate against. Validation will be attempted * in the order the schemas are returned. * * @return String[] Valid schemas */ protected abstract String[] getSchemas(); /** * Load the XML file using various strategies to locate it. * * If the filename begins with 'classpath:' it will attempt to load the filename as a resource. * * If the filename begins with 'file:' or 'http:', it will attempt to load the file name as a URL. * * If the filename begins with none of these, it will attempt to load it from the local * file system. If that fails, it will attempt to load it as a resource from the classpath. * * @throws Exception */ public Document load() throws Exception { return loadDocument(getFilename()); } /** * Load the configuration document. First try loading from explicitly defined locations * (the classpath or a URL), then fall back to attemping to load as a local file and * finally a resource in the classpath. * * @param filename String */ private Document loadDocument(String filename) throws Exception { Document doc = null; // Explicitly load from the classpath if (filename.startsWith(SOURCE_CLASSPATH)) { String resourceFilename = filename.substring(SOURCE_CLASSPATH.length()); doc = loadFromResource(resourceFilename); log.info("loadDocument(): loaded configuation from classpath"); return doc; } // Explicitly load from a URL if (filename.startsWith("file:") || filename.startsWith("http:")) { URL url = new java.net.URL(filename); doc = loadFromURL(url); log.info("loadDocument(): loaded configuation from URL"); return doc; } // Try loading as a file File file = new File(filename); if (file.exists()) { doc = loadFromFile(filename); log.info("loadDocument(): loaded configuation from file"); return doc; } // Try loading from the classpath doc = loadFromResource(filename); log.info("loadDocument(): loaded configuation from classpath"); return doc; } /** * Load the document from a resource in the classpath * * @param source * @throws Exception */ private Document loadFromResource(String filename) throws Exception { InputStream is = getClass().getResourceAsStream(filename); return loadXML(is); } /** * Load the document from a local file. * * @param source * @throws FileNotFoundException * @throws Exception */ private Document loadFromFile(String filename) throws Exception { FileInputStream is = new FileInputStream(filename); return loadXML(is); } /** * Load the document from a URL * * @param url */ private Document loadFromURL(URL url) throws Exception { // Open a connection to the URL URLConnection urlConn = url.openConnection(); // Get the input stream InputStream is = urlConn.getInputStream(); return loadXML(is); } /** * Set the CompoundDatabase filename. * * @param filename String */ public void setFilename(String filename) { this.filename = filename; } /** * Return the CompoundDatabase filename. * * @return String */ public String getFilename() { return filename; } /** * Set the validate XML property. This will cause the load() method to validate the * XML document against the XML schema. * * @param validateXML boolean */ public void setValidateXML(boolean validateXML) { this.validateXML = validateXML; } public boolean isValidateXML() { return validateXML; } private Document loadXML(InputStream is) throws Exception { // Load document DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setIgnoringElementContentWhitespace(true); factory.setIgnoringComments(true); factory.setValidating(false); // Don't use DTD validation DocumentBuilder docBuilder = factory.newDocumentBuilder(); ErrorHandler eh = new StrictErrorHandler(); docBuilder.setErrorHandler(eh); InputSource inputSource = new InputSource(is); inputSource.setPublicId(RimuDBNamespace.URI); inputSource.setSystemId(RimuDBNamespace.URI); Document document = docBuilder.parse(is); is.close(); // Determine the XML schema version from the XML document without validating Element root = document.getDocumentElement(); setDocumentSchema(lookupSchemaVersion(root)); // Validate the XML document and determine the XML Schema version if (isValidateXML()) { if (getDocumentSchema() != null) { // Validate the document against the schema found in the document SAXParseException saxParseException = validate(document, getDocumentSchema()); if (saxParseException != null) { throw saxParseException; } } else { setDocumentSchema(lookupSchemaByValidation(document)); } } return document; } /** * Validate the schema and return the schema that matches by trial and error. If * the document cannot be validated against possible schemas then throw an Exception. * * @param document Document * @return String */ public String lookupSchemaByValidation(Document document) throws Exception { SAXParseException saxParseException = null; String[] schemas = getSchemas(); for (int i = 0; i < schemas.length; i++) { saxParseException = validate(document, schemas[i]); if (saxParseException == null) { return schemas[i]; } } if (saxParseException != null) { throw saxParseException; } return null; } /** * Determine the XML schema version from the XML document without validating. * * @param root Element * @return String */ public String lookupSchemaVersion(Element root) { String s = root.getAttribute("xsi:schemaLocation"); if (s != null && s.length() > 0) { String parts[] = s.split(" "); if (parts != null && parts.length == 2) { String[] schemas = getSchemas(); for (int i = 0; i < schemas.length; i++) { if (parts[1].equals(schemas[i])) { return schemas[i]; } } } } return null; } /** * @param document * @param compoundDbSchemaUrl * @return SAXParseException * @throws Exception */ protected SAXParseException validate(Document document, String compoundDbSchemaUrl) throws Exception { // Compile a schema for the XSD SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // Look up the schema URL and find it's local resource file URL schemaURL = null; String schemaPath = RimuDBNamespace.lookupInternalSchema(compoundDbSchemaUrl); if (schemaPath != null) { schemaURL = getClass().getClassLoader().getResource(schemaPath); } else { schemaURL = new URL(compoundDbSchemaUrl); } Schema schema = schemaFactory.newSchema(schemaURL); // Validate the document against the schema Validator validator = schema.newValidator(); validator.setErrorHandler(new StrictErrorHandler()); try { validator.validate(new DOMSource(document), new DOMResult()); } catch (SAXParseException e) { return e; } return null; } public void setDocumentSchema(String validatedAgainstSchema) { documentSchema = validatedAgainstSchema; } public String getDocumentSchema() { return documentSchema; } }