/* The contents of this file are subject to the license and copyright terms * detailed in the license directory at the root of the source tree (also * available online at http://fedora-commons.org/license/). */ package fedora.server.validation; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.log4j.Logger; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import fedora.common.Constants; import fedora.server.errors.GeneralException; import fedora.server.errors.ObjectValidityException; /** * XML Schema validation for Digital Objects. * * @author Sandy Payette */ public class DOValidatorXMLSchema implements Constants, EntityResolver { /** Logger for this class. */ private static final Logger LOG = Logger.getLogger(DOValidatorXMLSchema.class.getName()); /** Constants used for JAXP 1.2 */ private static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; private URI schemaURI = null; public DOValidatorXMLSchema(String schemaPath) throws GeneralException { try { schemaURI = (new File(schemaPath)).toURI(); } catch (Exception e) { LOG.error("Error constructing validator", e); throw new GeneralException(e.getMessage()); } } public void validate(File objectAsFile) throws ObjectValidityException, GeneralException { try { validate(new InputSource(new FileInputStream(objectAsFile))); } catch (IOException e) { String msg = "DOValidatorXMLSchema returned error.\n" + "The underlying exception was a " + e.getClass().getName() + ".\n" + "The message was " + "\"" + e.getMessage() + "\""; throw new GeneralException(msg); } } public void validate(InputStream objectAsStream) throws ObjectValidityException, GeneralException { validate(new InputSource(objectAsStream)); } private void validate(InputSource objectAsSource) throws ObjectValidityException, GeneralException { InputSource doXML = objectAsSource; try { // XMLSchema validation via SAX parser SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); spf.setValidating(true); SAXParser sp = spf.newSAXParser(); sp.setProperty(JAXP_SCHEMA_LANGUAGE, XML_XSD.uri); // JAXP property for schema location sp .setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", schemaURI.toString()); XMLReader xmlreader = sp.getXMLReader(); xmlreader.setErrorHandler(new DOValidatorXMLErrorHandler()); xmlreader.setEntityResolver(this); xmlreader.parse(doXML); } catch (ParserConfigurationException e) { String msg = "DOValidatorXMLSchema returned parser error.\n" + "The underlying exception was a " + e.getClass().getName() + ".\n" + "The message was " + "\"" + e.getMessage() + "\""; throw new GeneralException(msg, e); } catch (SAXException e) { String msg = "DOValidatorXMLSchema returned validation exception.\n" + "The underlying exception was a " + e.getClass().getName() + ".\n" + "The message was " + "\"" + e.getMessage() + "\""; throw new ObjectValidityException(msg, e); } catch (Exception e) { String msg = "DOValidatorXMLSchema returned error.\n" + "The underlying error was a " + e.getClass().getName() + ".\n" + "The message was " + "\"" + e.getMessage() + "\""; throw new GeneralException(msg, e); } } /** * Resolve the entity if it's referring to a local schema. Otherwise, return * an empty InputSource. This behavior is required in order to ensure that * Xerces never attempts to load external schemas specified with * xsi:schemaLocation. It is not enough that we specify * processContents="skip" in our own schema. */ public InputSource resolveEntity(String publicId, String systemId) { if (systemId != null && systemId.startsWith("file:")) { return null; } else { return new InputSource(); } } }