/* ESXX - The friendly ECMAscript/XML Application Server Copyright (C) 2007-2015 Martin Blom <martin@blom.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.esxx; import com.thaiopensource.util.PropertyMap; import com.thaiopensource.util.PropertyMapBuilder; import com.thaiopensource.validate.IncorrectSchemaException; import com.thaiopensource.validate.SchemaReader; import com.thaiopensource.validate.ValidateProperty; import com.thaiopensource.validate.Validator; import com.thaiopensource.validate.auto.AutoSchemaReader; import com.thaiopensource.validate.rng.CompactSchemaReader; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Collection; import java.util.Date; import java.util.HashSet; import org.esxx.util.URIResolver; import org.w3c.dom.Node; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.ErrorHandler; import java.util.logging.Level; // import javax.xml.transform.*; // import javax.xml.transform.stream.StreamSource; // import net.sf.saxon.s9api.*; // import org.esxx.ESXX; // import org.esxx.util.URIResolver; public class Schema implements org.esxx.cache.Cached { public Schema(final URI uri, InputStream is, String type) throws IOException { esxx = ESXX.getInstance(); if (is == null) { is = esxx.openCachedURI(uri); } this.uri = uri; started = new Date(); externalURIs.add(uri); try { PropertyMapBuilder pmb = new PropertyMapBuilder(); URIResolver res = new URIResolver(esxx, uri, externalURIs); ErrorHandler eh = new ErrorHandler() { public void error(SAXParseException ex) throws SAXParseException { throw ex; } public void fatalError(SAXParseException ex) throws SAXParseException { throw ex; } public void warning(SAXParseException ex) { esxx.getLogger().logp(Level.WARNING, uri.toString(), null, "Line " + ex.getLineNumber() + ", column " + ex.getColumnNumber() + ": " + ex.getMessage()); } }; pmb.put(ValidateProperty.ENTITY_RESOLVER, res); pmb.put(ValidateProperty.ERROR_HANDLER, eh); pmb.put(ValidateProperty.URI_RESOLVER, res); propertyMap = pmb.toPropertyMap(); SchemaReader sr = ("application/relax-ng-compact-syntax".matches(type) || "application/x-rnc".matches(type) ? CompactSchemaReader.getInstance() : new AutoSchemaReader()); schema = sr.createSchema(new InputSource(is), propertyMap); } catch (SAXException ex) { throw new IOException("Failed to load schema: " + ex.getMessage(), ex); } catch (IncorrectSchemaException ex) { throw new IOException("Failed to load schema: " + ex.getMessage(), ex); } } @Override public String toString() { return "[" + this.getClass().getName() + ": " + uri + "]"; } @Override /* Cached */ public String getFilename() { return uri.toString(); } @Override /* Cached */ public synchronized void logUsage(long start_time) { ++invocations; lastAccessed = System.currentTimeMillis(); if (start_time != 0) { executionTime += (lastAccessed - start_time); } } @Override /* Cached */ public Collection<URI> getExternalURIs() { return externalURIs; } @Override /* Cached */ public synchronized JMXBean getJMXBean() { if (jmxBean == null) { jmxBean = new JMXBean(); } return jmxBean; } public void validate(Node node) { Validator v = schema.createValidator(propertyMap); XMLReader r = new gnu.xml.util.DomParser(node); ContentHandler h = v.getContentHandler(); r.setContentHandler(h); if (v.getDTDHandler() != null) { r.setDTDHandler(v.getDTDHandler()); } try { if (node.getNodeType() != Node.DOCUMENT_NODE) { h.startDocument(); } r.parse("ignored"); if (node.getNodeType() != Node.DOCUMENT_NODE) { h.endDocument(); } } catch (SAXParseException ex) { throw new ESXXException(422 /* Unprocessable Entity */, ex.getMessage(), ex); } catch (IOException ex) { throw new ESXXException("IOException: " + ex.getMessage(), ex); } catch (SAXException ex) { throw new ESXXException("SAXException: " + ex.getMessage(), ex); } } private class JMXBean extends javax.management.StandardEmitterMBean implements org.esxx.jmx.SchemaMXBean { public JMXBean() { super(org.esxx.jmx.SchemaMXBean.class, true, new javax.management.NotificationBroadcasterSupport()); } @Override public String getFilename() { return Schema.this.getFilename(); } @Override public void unloadSchema() { esxx.removeCachedSchema(Schema.this); } @Override public org.esxx.jmx.ApplicationStats getStatistics() { synchronized (Schema.this) { return new org.esxx.jmx.ApplicationStats(invocations, executionTime, started, new Date(lastAccessed)); } } } private ESXX esxx; private JMXBean jmxBean; private URI uri; private com.thaiopensource.validate.Schema schema; private PropertyMap propertyMap; private HashSet<URI> externalURIs = new HashSet<URI>(); private long invocations; private long executionTime; private Date started; private long lastAccessed; }