/* * Copyright (C) 2005-2012 BetaCONCEPT Limited * * This file is part of Astroboa. * * Astroboa 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. * * Astroboa 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 Astroboa. If not, see <http://www.gnu.org/licenses/>. */ package org.betaconceptframework.astroboa.configuration; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.Map; import javax.xml.XMLConstants; import org.apache.commons.lang.StringUtils; import org.betaconceptframework.astroboa.api.model.exception.CmsException; import org.betaconceptframework.astroboa.util.CmsConstants; import org.slf4j.LoggerFactory; import org.w3c.dom.bootstrap.DOMImplementationRegistry; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSInput; import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * When the repository registry is initialized, Astroboa configuration file is loaded and validated * against the configuration XML Schema. * * During the validation process, XSDs are validated by the parser. Part of the validation process * is to import all necessary XSDs or DTDs dependencies. * Some of these XSDs (XML Schema's XSD, etc) or DTDs are located externally and thus, can be imported * only when an Internet access * is available. When this is not the case, XSD parser cannot complete validation and throws an exception. * * To avoid this situation (which causes a build to fail or even worse does not allow Astroboa to run), * Astroboa identifies whether these files can be accessed and if they cannot, it feeds the parser with * their copy which is located in this directory. * * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ public class W3CRelatedSchemaEntityResolver implements EntityResolver, LSResourceResolver { private String xmlSchemaHomeDir = CmsConstants.FORWARD_SLASH+"META-INF"+CmsConstants.FORWARD_SLASH+"xml-schema-dtd"; private DOMImplementationRegistry registry; private Map<String, URL> schemaURLsPerPublicId = new HashMap<String, URL>(); public W3CRelatedSchemaEntityResolver(){ try { registry = DOMImplementationRegistry.newInstance(); } catch (Exception e) { throw new CmsException(e); } } /** * Resolves Entities with the following properties * * publicId = http://www.w3.org/XML/1998/namespace * systemId = http://www.w3.org/2001/03/xml.xsd * * publicId = datatypes * systemId = http://www.w3.org/2001/03/datatypes.dtd * * publicId = -//W3C//DTD XMLSCHEMA 200102//EN * systemId = http://www.w3.org/2001/03/XMLSchema.dtd * * If above schemata are not reachable (probably due to a lack of Internet access), * they are loaded from the package * * @return * @throws IOException */ @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { if (StringUtils.equals(systemId, CmsConstants.XML_SCHEMA_LOCATION)){ return locateEntity(CmsConstants.XML_SCHEMA_LOCATION, XMLConstants.XML_NS_URI); } if (StringUtils.equals(systemId, CmsConstants.XML_SCHEMA_DTD_LOCATION)){ return locateEntity(CmsConstants.XML_SCHEMA_DTD_LOCATION, "-//W3C//DTD XMLSCHEMA 200102//EN"); } if (StringUtils.equals(systemId, CmsConstants.XML_DATATYPES_DTD_LOCATION)){ return locateEntity(CmsConstants.XML_DATATYPES_DTD_LOCATION, "datatypes"); } return null; } private InputSource locateEntity(String systemId, String publicId) throws IOException{ URL xsdOrDtdLocation = null; if (publicId != null && schemaURLsPerPublicId.containsKey(publicId)){ xsdOrDtdLocation = schemaURLsPerPublicId.get(publicId); } if (systemId == null){ return null; } String xsdOrDtdFilename = (systemId.contains(CmsConstants.FORWARD_SLASH) ? StringUtils.substringAfterLast(systemId, CmsConstants.FORWARD_SLASH) : systemId); //Check if schema is available locally if (xsdOrDtdLocation == null){ xsdOrDtdLocation = this.getClass().getResource(xmlSchemaHomeDir+CmsConstants.FORWARD_SLASH+xsdOrDtdFilename); } //Try on the WEB if (xsdOrDtdLocation == null){ xsdOrDtdLocation = new URL(systemId); } try { InputSource is = new InputSource( xsdOrDtdLocation.openStream() ); //System Id is the path of this URL is.setSystemId(xsdOrDtdLocation.toString()); is.setPublicId(publicId); schemaURLsPerPublicId.put(publicId, xsdOrDtdLocation); return is; } catch (Throwable isEx) { //Log a warning and let the Xerces parser to locate the schema LoggerFactory.getLogger(getClass()).warn("Unable to resolve schema for "+publicId + " "+ systemId + " in URL "+xsdOrDtdLocation.toString(), isEx); //continue with parser provided by Java. If schema cannot be located, an exception will be thrown return null; } } @Override public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { InputSource entity; try { entity = locateEntity(systemId, publicId); } catch (IOException e) { throw new CmsException(e); } if (entity == null || entity.getByteStream() == null){ return null; } DOMImplementationLS domImplementationLS = (DOMImplementationLS) registry.getDOMImplementation("LS"); LSInput lsInput = domImplementationLS.createLSInput(); lsInput.setByteStream(entity.getByteStream()); lsInput.setSystemId(systemId); return lsInput; } }