/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org */ package org.jboss.metadata; import java.net.URL; import java.io.IOException; import java.io.InputStream; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import org.w3c.dom.Document; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.InputSource; import org.jboss.deployment.DeploymentException; import org.jboss.deployment.JBossEntityResolver; import org.jboss.logging.Logger; /** XmlFileLoader class is used to read ejb-jar.xml, standardjboss.xml, jboss.xml * files, process them using DTDs and create ApplicationMetaData object for * future use. It also provides the local entity resolver for the JBoss * specific DTDs. * * @author Bill Burke * @author Sebastien Alborini * @author Wolfgang Werner * @author Darius Davidavicius * @author Scott Stark * @version $Revision: 1.28.4.8 $ */ public class XmlFileLoader { // Constants ----------------------------------------------------- // Attributes ---------------------------------------------------- private static boolean defaultValidateDTDs = false; private static Logger log = Logger.getLogger(XmlFileLoader.class); private ClassLoader classLoader; private ApplicationMetaData metaData; private boolean validateDTDs; // Static -------------------------------------------------------- public static boolean getDefaultValidateDTDs() { return defaultValidateDTDs; } public static void setDefaultValidateDTDs(boolean validate) { defaultValidateDTDs = validate; } // Constructors -------------------------------------------------- public XmlFileLoader() { this(defaultValidateDTDs); } public XmlFileLoader(boolean validateDTDs) { this.validateDTDs = validateDTDs; } // Public -------------------------------------------------------- public ApplicationMetaData getMetaData() { return metaData; } /** * Set the class loader * * @param ClassLoader cl - class loader */ public void setClassLoader(ClassLoader cl) { classLoader = cl; } /** * Gets the class loader * * @return ClassLoader - the class loader */ public ClassLoader getClassLoader() { return classLoader; } /** Get the flag indicating that ejb-jar.dtd, jboss.dtd & * jboss-web.dtd conforming documents should be validated * against the DTD. */ public boolean getValidateDTDs() { return validateDTDs; } /** Set the flag indicating that ejb-jar.dtd, jboss.dtd & * jboss-web.dtd conforming documents should be validated * against the DTD. */ public void setValidateDTDs(boolean validate) { this.validateDTDs = validate; } /** * load() * * This method creates the ApplicationMetaData. * The configuration files are found in the classLoader. * The default jboss.xml and jaws.xml files are always read first, then we override * the defaults if the user provides them */ public ApplicationMetaData load() throws Exception { // create the metadata metaData = new ApplicationMetaData(); // Load ejb-jar.xml // we can always find the files in the classloader URL ejbjarUrl = getClassLoader().getResource("META-INF/ejb-jar.xml"); if (ejbjarUrl == null) { throw new DeploymentException("no ejb-jar.xml found"); } Document ejbjarDocument = getDocumentFromURL(ejbjarUrl); // the url may be used to report errors metaData.setUrl(ejbjarUrl); metaData.importEjbJarXml(ejbjarDocument.getDocumentElement()); // Load jbossdefault.xml from the default classLoader // we always load defaults first // we use the context classloader, because this guy has to know where // this file is URL defaultJbossUrl = Thread.currentThread().getContextClassLoader().getResource("standardjboss.xml"); if (defaultJbossUrl == null) { throw new DeploymentException("no standardjboss.xml found"); } Document defaultJbossDocument = null; try { defaultJbossDocument = getDocumentFromURL(defaultJbossUrl); metaData.setUrl(defaultJbossUrl); metaData.importJbossXml(defaultJbossDocument.getDocumentElement()); } catch (Exception ex) { log.error("failed to load standardjboss.xml. There could be a syntax error.", ex); throw ex; } // Load jboss.xml // if this file is provided, then we override the defaults try { URL jbossUrl = getClassLoader().getResource("META-INF/jboss.xml"); if (jbossUrl != null) { Document jbossDocument = getDocumentFromURL(jbossUrl); metaData.setUrl(jbossUrl); metaData.importJbossXml(jbossDocument.getDocumentElement()); } } catch (Exception ex) { log.error("failed to load jboss.xml. There could be a syntax error.", ex); throw ex; } return metaData; } /** Invokes getDocument(url, defaultValidateDTDs) * */ public static Document getDocument(URL url) throws DeploymentException { return getDocument(url, defaultValidateDTDs); } /** Get the xml file from the URL and parse it into a Document object. * Calls new XmlFileLoader(validateDTDs).getDocumentFromURL(url); * @param url, the URL from which the xml doc is to be obtained. * @return Document */ public static Document getDocument(URL url, boolean validateDTDs) throws DeploymentException { XmlFileLoader loader = new XmlFileLoader(validateDTDs); return loader.getDocumentFromURL(url); } /** Get the xml file from the URL and parse it into a Document object. * Calls getDocument(new InputSource(url.openStream()), url.getPath()) * with the InputSource.SystemId set to url.toExternalForm(). * * @param url, the URL from which the xml doc is to be obtained. * @return Document */ public Document getDocumentFromURL(URL url) throws DeploymentException { InputStream is = null; try { is = url.openStream(); return getDocument(is, url.toExternalForm()); } catch (IOException e) { throw new DeploymentException("Failed to obtain xml doc from URL", e); } } /** Parses the xml document in is to create a DOM Document. DTD validation * is enabled if validateDTDs is true and we install an EntityResolver and * ErrorHandler to resolve J2EE DTDs and handle errors. We also create an * InputSource for the InputStream and set the SystemId URI to the inPath * value. This allows relative entity references to be resolved against the * inPath URI. The is argument will be closed. * * @param is, the InputStream containing the xml descriptor to parse * @param inPath, the path information for the xml doc. This is used as the * InputSource SystemId URI for resolving relative entity references. * @return Document */ public Document getDocument(InputStream is, String inPath) throws DeploymentException { InputSource is2 = new InputSource(is); is2.setSystemId(inPath); Document doc = null; try { doc = getDocument(is2, inPath); } finally { // close the InputStream to get around "too many open files" errors // with large heaps try { if( is != null ) is.close(); } catch (Exception e) { // ignore } } return doc; } /** Parses the xml document in is to create a DOM Document. DTD validation * is enabled if validateDTDs is true and we install an EntityResolver and * ErrorHandler to resolve J2EE DTDs and handle errors. We also create an * InputSource for the InputStream and set the SystemId URI to the inPath * value. This allows relative entity references to be resolved against the * inPath URI. * * @param is, the InputSource containing the xml descriptor to parse * @param inPath, the path information for the xml doc. This is used for * only for error reporting. * @return Document */ public Document getDocument(InputSource is, String inPath) throws DeploymentException { try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); // Enable DTD validation based on our validateDTDs flag docBuilderFactory.setValidating(validateDTDs); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); JBossEntityResolver lr = new JBossEntityResolver(); LocalErrorHandler eh = new LocalErrorHandler( inPath, lr ); docBuilder.setEntityResolver(lr); docBuilder.setErrorHandler(eh ); Document doc = docBuilder.parse(is); if(validateDTDs && eh.hadError()) { throw new DeploymentException("Invalid XML: file=" + inPath); } return doc; } catch (DeploymentException e) { throw e; } catch (SAXParseException e) { log.error(e.getMessage()+":"+e.getColumnNumber()+":"+e.getLineNumber(), e); throw new DeploymentException(e.getMessage(), e); } catch (SAXException e) { System.out.println(e.getException()); throw new DeploymentException(e.getMessage(), e); } catch (Exception e) { throw new DeploymentException(e.getMessage(), e); } } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- /** Local error handler for entity resolver to DocumentBuilder parser. * Error is printed to output just if DTD was detected in the XML file. * If DTD was not found in XML file it is assumed that the EJB builder * doesn't want to use DTD validation. Validation may have been enabled via * validateDTDs flag so we look to the hasDTD() function in the LocalResolver * and reject errors if DTD not used. **/ private static class LocalErrorHandler implements ErrorHandler { // The xml file being parsed private String theFileName; private JBossEntityResolver localResolver; private boolean error; public LocalErrorHandler( String inFileName, JBossEntityResolver localResolver ) { this.theFileName = inFileName; this.localResolver = localResolver; this.error = false; } public void error(SAXParseException exception) { if ( localResolver.hasDTD() ) { this.error = true; log.error("XmlFileLoader: File " + theFileName + " process error. Line: " + String.valueOf(exception.getLineNumber()) + ". Error message: " + exception.getMessage() ); }//end if } public void fatalError(SAXParseException exception) { if ( localResolver.hasDTD() ) { this.error = true; log.error("XmlFileLoader: File " + theFileName + " process fatal error. Line: " + String.valueOf(exception.getLineNumber()) + ". Error message: " + exception.getMessage() ); }//end if } public void warning(SAXParseException exception) { if ( localResolver.hasDTD() ) { this.error = true; log.error("XmlFileLoader: File " + theFileName + " process warning. Line: " + String.valueOf(exception.getLineNumber()) + ". Error message: " + exception.getMessage() ); }//end if } public boolean hadError() { return error; } }// end class LocalErrorHandler }