diff options
Diffstat (limited to 'config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java')
-rw-r--r-- | config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java | 219 |
1 files changed, 37 insertions, 182 deletions
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java index 70da2f2e92e..d0cca38b375 100644 --- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java +++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/SchemaValidator.java @@ -6,214 +6,40 @@ import com.thaiopensource.util.PropertyMapBuilder; import com.thaiopensource.validate.ValidateProperty; import com.thaiopensource.validate.ValidationDriver; import com.thaiopensource.validate.rng.CompactSchemaReader; -import com.yahoo.component.Version; import com.yahoo.config.application.api.DeployLogger; -import com.yahoo.io.IOUtils; import com.yahoo.io.reader.NamedReader; -import com.yahoo.log.LogLevel; -import static com.yahoo.vespa.defaults.Defaults.getDefaults; import com.yahoo.yolean.Exceptions; -import org.osgi.framework.Bundle; -import org.osgi.framework.FrameworkUtil; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; + import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Reader; -import java.net.JarURLConnection; -import java.net.URL; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.logging.Level; -import java.util.logging.Logger; /** - * Validates xml files against one schema. + * Validates xml files against a schema. * * @author tonytv */ public class SchemaValidator { - public static final String schemaDirBase = System.getProperty("java.io.tmpdir", File.separator + "tmp" + File.separator + "vespa"); - static final String servicesXmlSchemaName = "services.rnc"; - static final String hostsXmlSchemaName = "hosts.rnc"; - static final String deploymentXmlSchemaName = "deployment.rnc"; - static final String validationOverridesXmlSchemaName = "validation-overrides.rnc"; private final CustomErrorHandler errorHandler = new CustomErrorHandler(); private final ValidationDriver driver; - private DeployLogger deployLogger; - private static final Logger log = Logger.getLogger(SchemaValidator.class.getName()); - - /** - * Initializes the validator by using the given file as schema file - * @param schema a schema file in RNC format - * @param logger a logger - * @param vespaVersion the version of Vespa we should validate against - */ - public SchemaValidator(String schema, DeployLogger logger, Version vespaVersion) { - this.deployLogger = logger; - driver = new ValidationDriver(PropertyMap.EMPTY, instanceProperties(), CompactSchemaReader.getInstance()); - File schemaDir = new File(schemaDirBase); - try { - schemaDir = saveSchemasFromJar(new File(SchemaValidator.schemaDirBase), vespaVersion); - } catch (IOException e) { - throw new RuntimeException(e); - } - loadSchema(new File(schemaDir + File.separator + "schema" + File.separator + schema)); - IOUtils.recursiveDeleteDir(schemaDir); - } + private final DeployLogger deployLogger; /** * Initializes the validator by using the given file as schema file - * @param schema a schema file in RNC format - * @param vespaVersion the version we should validate against - * @throws IOException if it is not possible to read schema files - */ - public SchemaValidator(String schema, Version vespaVersion) throws IOException { - this(schema, new BaseDeployLogger(), vespaVersion); - } - - /** - * Create a validator for services.xml for tests - * @throws IOException if it is not possible to read schema files - */ - public static SchemaValidator createTestValidatorServices(Version vespaVersion) throws IOException { - return new SchemaValidator(servicesXmlSchemaName, vespaVersion); - } - - /** - * Create a validator for hosts.xml for tests - * @throws IOException if it is not possible to read schema files - */ - public static SchemaValidator createTestValidatorHosts(Version vespaVersion) throws IOException { - return new SchemaValidator(hostsXmlSchemaName, vespaVersion); - } - - /** - * Create a validator for deployment.xml for tests * + * @param schemaFile schema file * @throws IOException if it is not possible to read schema files */ - public static SchemaValidator createTestValidatorDeployment(Version vespaVersion) throws IOException { - return new SchemaValidator(deploymentXmlSchemaName, vespaVersion); - } - - private class CustomErrorHandler implements ErrorHandler { - volatile String fileName; - - public void warning(SAXParseException e) throws SAXException { - deployLogger.log(Level.WARNING, message(e)); - } - - public void error(SAXParseException e) throws SAXException { - throw new IllegalArgumentException(message(e)); - } - - public void fatalError(SAXParseException e) throws SAXException { - throw new IllegalArgumentException(message(e)); - } - - private String message(SAXParseException e) { - return "XML error in " + fileName + ": " + - Exceptions.toMessageString(e) - + " [" + e.getLineNumber() + ":" + e.getColumnNumber() + "]"; - } - } - - /** - * Look for the schema files that should be in vespa-model.jar and saves them on temp dir. - * - * @return the directory the schema files are stored in - * @throws IOException if it is not possible to read schema files - */ - private File saveSchemasFromJar(File tmpBase, Version vespaVersion) throws IOException { - final Class<? extends SchemaValidator> schemaValidatorClass = this.getClass(); - final ClassLoader classLoader = schemaValidatorClass.getClassLoader(); - Enumeration<URL> uris = classLoader.getResources("schema"); - if (uris==null) return null; - File tmpDir = java.nio.file.Files.createTempDirectory(tmpBase.toPath(), "vespa").toFile(); - log.log(LogLevel.DEBUG, "Saving schemas to " + tmpDir); - while(uris.hasMoreElements()) { - URL u = uris.nextElement(); - log.log(LogLevel.DEBUG, "uri for resource 'schema'=" + u.toString()); - if ("jar".equals(u.getProtocol())) { - JarURLConnection jarConnection = (JarURLConnection) u.openConnection(); - JarFile jarFile = jarConnection.getJarFile(); - for (Enumeration<JarEntry> entries = jarFile.entries(); - entries.hasMoreElements();) { - - JarEntry je=entries.nextElement(); - if (je.getName().startsWith("schema/") && je.getName().endsWith(".rnc")) { - writeContentsToFile(tmpDir, je.getName(), jarFile.getInputStream(je)); - } - } - jarFile.close(); - } else if ("bundle".equals(u.getProtocol())) { - Bundle bundle = FrameworkUtil.getBundle(schemaValidatorClass); - log.log(LogLevel.DEBUG, classLoader.toString()); - log.log(LogLevel.DEBUG, "bundle=" + bundle); - // TODO: Hack to handle cases where bundle=null - if (bundle == null) { - File schemaPath; - if (vespaVersion.getMajor() == 5) { - schemaPath = new File(getDefaults().underVespaHome("share/vespa/schema/version/5.x/schema/")); - } else { - schemaPath = new File(getDefaults().underVespaHome("share/vespa/schema/")); - } - log.log(LogLevel.DEBUG, "Using schemas found in " + schemaPath); - copySchemas(schemaPath, tmpDir); - } else { - log.log(LogLevel.DEBUG, String.format("Saving schemas for model bundle %s:%s", bundle.getSymbolicName(), bundle - .getVersion())); - for (Enumeration<URL> entries = bundle.findEntries("schema", "*.rnc", true); - entries.hasMoreElements(); ) { - - URL url = entries.nextElement(); - writeContentsToFile(tmpDir, url.getFile(), url.openStream()); - } - } - } else if ("file".equals(u.getProtocol())) { - File schemaPath = new File(u.getPath()); - copySchemas(schemaPath, tmpDir); - } - } - return tmpDir; - } - - private static void copySchemas(File from, File to) throws IOException { - // TODO: only copy .rnc files. - if (! from.exists()) throw new IOException("Could not find schema source directory '" + from + "'"); - if (! from.isDirectory()) throw new IOException("Schema source '" + from + "' is not a directory"); - File sourceFile = new File(from, servicesXmlSchemaName); - if (! sourceFile.exists()) throw new IOException("Schema source file '" + sourceFile + "' not found"); - IOUtils.copyDirectoryInto(from, to); - } - - private static void writeContentsToFile(File outDir, String outFile, InputStream inputStream) throws IOException { - String contents = IOUtils.readAll(new InputStreamReader(inputStream)); - File out = new File(outDir, outFile); - IOUtils.writeFile(out, contents, false); - } - - private void loadSchema(File schemaFile) { - try { - driver.loadSchema(ValidationDriver.fileInputSource(schemaFile)); - } catch (SAXException e) { - throw new RuntimeException("Invalid schema '" + schemaFile + "'", e); - } catch (IOException e) { - throw new RuntimeException("IO error reading schema '" + schemaFile + "'", e); - } - } - - private PropertyMap instanceProperties() { - PropertyMapBuilder builder = new PropertyMapBuilder(); - builder.put(ValidateProperty.ERROR_HANDLER, errorHandler); - return builder.toPropertyMap(); + SchemaValidator(File schemaFile, DeployLogger deployLogger) throws IOException, SAXException { + this.deployLogger = deployLogger; + this.driver = new ValidationDriver(PropertyMap.EMPTY, instanceProperties(), CompactSchemaReader.getInstance()); + driver.loadSchema(ValidationDriver.fileInputSource(schemaFile)); } public void validate(File file) throws IOException { @@ -246,4 +72,33 @@ public class SchemaValidator { "XML error in " + (fileName == null ? " input" : fileName) + ": " + Exceptions.toMessageString(e)); } } + + private PropertyMap instanceProperties() { + PropertyMapBuilder builder = new PropertyMapBuilder(); + builder.put(ValidateProperty.ERROR_HANDLER, errorHandler); + return builder.toPropertyMap(); + } + + private class CustomErrorHandler implements ErrorHandler { + volatile String fileName; + + public void warning(SAXParseException e) throws SAXException { + deployLogger.log(Level.WARNING, message(e)); + } + + public void error(SAXParseException e) throws SAXException { + throw new IllegalArgumentException(message(e)); + } + + public void fatalError(SAXParseException e) throws SAXException { + throw new IllegalArgumentException(message(e)); + } + + private String message(SAXParseException e) { + return "XML error in " + fileName + ": " + + Exceptions.toMessageString(e) + + " [" + e.getLineNumber() + ":" + e.getColumnNumber() + "]"; + } + } + } |