/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 *******************************************************************************/ /** * */ package org.ebayopensource.turmeric.runtime.common.impl.utils; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.ebayopensource.turmeric.runtime.common.exceptions.ErrorDataFactory; import org.ebayopensource.turmeric.runtime.common.exceptions.ErrorUtils; import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceCreationException; import org.ebayopensource.turmeric.runtime.common.impl.internal.config.SchemaValidationLevel; import org.ebayopensource.turmeric.runtime.common.registration.ClassLoaderRegistry; import org.ebayopensource.turmeric.runtime.errorlibrary.ErrorConstants; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import com.ebay.kernel.resource.ResourceUtil; /** * @author rpallikonda * */ public class ParseUtils { private static final Logger LOG = Logger.getLogger(ParseUtils.class.getName()); public static final String SYS_PROP_CONFIG_SCHEMA_CHECK = "org.ebayopensource.turmeric.runtime.common.impl.config.schemacheck"; protected static SchemaValidationLevel s_schemaCheckLevel; static { String schemaCheckStr = System.getProperty(SYS_PROP_CONFIG_SCHEMA_CHECK); if (schemaCheckStr == null) { s_schemaCheckLevel = SchemaValidationLevel.NONE; } else { schemaCheckStr = schemaCheckStr.toUpperCase(); s_schemaCheckLevel = SchemaValidationLevel.valueOf(schemaCheckStr); } } /** * Used for testing */ public static void reloadSchemaCheckLevel() { String schemaCheckStr = System.getProperty(SYS_PROP_CONFIG_SCHEMA_CHECK); if (schemaCheckStr != null) { schemaCheckStr = schemaCheckStr.toUpperCase(); s_schemaCheckLevel = SchemaValidationLevel.valueOf(schemaCheckStr); } } public static SchemaValidationLevel getSchemaValidationLevel() { return s_schemaCheckLevel; } public static SchemaValidationLevel getSchemaCheckLevel() { return s_schemaCheckLevel; } private static ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } static Pattern GLOBAL = Pattern.compile(".+/Global.+"); //static Pattern PROJECT1 = Pattern.compile(".+/WebUtilityService_Test.+"); //static Pattern PROJECT2 = Pattern.compile(".+/WebUtilityService.+"); static Pattern PROJECT_PATTERNS[] = {Pattern.compile("META-INF/soa/services/config/(\\w+)/ServiceConfig.xml"), Pattern.compile("META-INF/soa/client/config/(\\w+)/ClientConfig.xml"), Pattern.compile("META-INF/soa/common/config/(\\w+)/TypeMappings.xml"), Pattern.compile("META-INF/soa/services/config/(\\w+)/SecurityPolicy.xml"), Pattern.compile("META-INF/soa/services/config/(\\w+)/CachePolicy.xml"), Pattern.compile("META-INF/soa/common/config/(\\w+)/service_metadata.properties"), Pattern.compile("META-INF/soa/services/config/(\\w+)/service_metadata.properties") }; public static InputStream getFileStream(String fileName) throws ServiceCreationException { InputStream inStream = null; if (fileName.startsWith("$config/")) { String relPath = fileName.substring(8); URL url = null; try { url = ResourceUtil.getResource("config", relPath); if (url != null) { inStream = url.openStream(); } } catch (IOException ioExc) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_CANNOT_LOAD_FILE, ErrorConstants.ERRORDOMAIN, new Object[] {fileName}), ioExc); } } else { inStream = getFileStreamInternal(fileName); } return inStream; } /** * Internal method, which will try to use 3 ClassLoaders to get the file. * 1) If a ClassLoader for the provided file is registered in ClassLoaderRegistry, * then it should be used * 2) Otherwise we should try to load the file by using a ClassLoader of the current bundle. * 3) And if that attempt fails as well, then we will try * Thread.currentThread().getContextClassLoader() * * @param fileName * @return */ private static InputStream getFileStreamInternal(String fileName) { StringBuffer infoLoad = new StringBuffer("FILE: " + fileName); InputStream inStream = null; try { ClassLoader classLoader = ClassLoaderRegistry.instanceOf().getClassLoaderForFile(fileName); if (classLoader != null) { infoLoad.append(" - exact name"); inStream = classLoader.getResourceAsStream(fileName); } else { infoLoad.append(" - trying to use this bundle (SOA Runtime) ClassLoader"); classLoader = ParseUtils.class.getClassLoader(); inStream = classLoader.getResourceAsStream(fileName); if (inStream == null) { infoLoad.append(" - trying to use default(\"thread\") ClassLoader"); classLoader = getClassLoader(); inStream = classLoader.getResourceAsStream(fileName); } } if (inStream != null) { infoLoad.append(". Found!\n"); } else { infoLoad.append(". Not found...\n"); } } catch (RuntimeException e) { infoLoad.append(". Error: " + e.getMessage() + "\n"); throw e; } finally { ClassLoaderRegistry.instanceOf().writeToOut(infoLoad.toString()); } return inStream; } /** * Loads the specified resource either as a file using {@link ResourceUtil#getResource(String, String) * ResourceUtil.getResource("config", filePath)}, if it starts with "$config"), or * as a resource using {@link #getClassLoader() getClassLoader()}.{@link ClassLoader#getResourceAsStream(String) * getResourceAsStream(filePath)}. If none succeeds, it'll also attempt {@link #getClassLoader() getClassLoader()}.{@link ClassLoader#getResourceAsStream(String) * getResourceAsStream(resourcePath)}. * @param filePath the file to load as a file. * @param resourcePath the resource to load as a resource - ignored if filePath could be loaded. * @param lenient <code>true</code> to attempt to load the resource if file was not found, <code>false</code> * to throw if {@link ResourceUtil#getResource(<configFolder>, filePath)} failed. * @return the contents of the file as an {@link InputStream}. * * @throws ServiceCreationException if non-lenient and * {@link ResourceUtil#getResource(<configFolder>, filePath)} failed. */ public static InputStream getFileOrResourceStream(String filePath, String resourcePath, boolean lenient) throws ServiceCreationException { InputStream inStream = null; if (filePath.startsWith("$config/")) { String relPath = filePath.substring(8); URL url = null; try { url = ResourceUtil.getResource("config", relPath); if (url != null) { inStream = url.openStream(); } } catch (IOException ioExc) { ServiceCreationException e = new ServiceCreationException( ErrorDataFactory.createErrorData(ErrorConstants.CFG_CANNOT_LOAD_FILE, ErrorConstants.ERRORDOMAIN, new Object[] {filePath}), ioExc); if (!lenient) { throw e; } LOG.log(Level.WARNING, "Could not load resource " + filePath + " due to error: " + ioExc.toString() + "(" + (ioExc.getMessage() == null ? "" : ioExc.getMessage()) + ")", ioExc); } } else { inStream = getFileStreamInternal(filePath); } if (inStream == null) { inStream = getFileStreamInternal(resourcePath); } return inStream; } public static synchronized Document parseConfig(String fileName, String schemaName, boolean isOptional, String topLevelName, SchemaValidationLevel checkLevel) throws ServiceCreationException { InputStream inStream = getFileStream(fileName); if (inStream == null) { if (isOptional) { return null; } throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_CANNOT_LOAD_FILE, ErrorConstants.ERRORDOMAIN, new Object[] {fileName})); } return parseConfig(inStream, fileName, schemaName, topLevelName, checkLevel); } public static Document parseConfig(InputStream in, String assocURL, String schemaName, String topLevelName, SchemaValidationLevel checkLevel) throws ServiceCreationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); Document result = null; try { DocumentBuilder builder = factory.newDocumentBuilder(); ErrorHandler errorHandler = new ParseErrorHandler(assocURL, checkLevel); builder.setErrorHandler(errorHandler); result = builder.parse(in); } catch (ParserConfigurationException e) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_PARSE_ERROR, ErrorConstants.ERRORDOMAIN, new Object[] {assocURL, e.toString()}), e); } catch (SAXException e) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_PARSE_ERROR, ErrorConstants.ERRORDOMAIN, new Object[] {assocURL, e.toString()}), e); } catch (IOException e) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_PARSE_ERROR, ErrorConstants.ERRORDOMAIN, new Object[] {assocURL, e.toString()}), e); } Element docElement = result.getDocumentElement(); if (docElement == null) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_VALIDATION_ERROR, ErrorConstants.ERRORDOMAIN, new Object[] {assocURL, "Document has no top-level element"})); } validate(schemaName, assocURL, docElement, checkLevel); if (!docElement.getNodeName().equals(topLevelName)) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_VALIDATION_ERROR, ErrorConstants.ERRORDOMAIN, new Object[] {assocURL, "Top-level element name: " + docElement.getNodeName() + "; expected: " + topLevelName})); } return result; } private static void validate(String schemaName, String filename, Node document, SchemaValidationLevel checkLevel) throws ServiceCreationException { if (checkLevel.equals(SchemaValidationLevel.NONE)) { return; } ClassLoader classLoader = getClassLoader(); URL url = classLoader.getResource(schemaName); if (url == null) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_CANNOT_LOAD_FILE, ErrorConstants.ERRORDOMAIN, new Object[] {schemaName})); } SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); try { Schema schema = factory.newSchema(url); Validator validator = schema.newValidator(); ErrorHandler errorHandler = new ParseErrorHandler(filename, checkLevel); validator.setErrorHandler(errorHandler); validator.validate(new DOMSource(document)); } catch (SAXException se) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_PARSE_ERROR, ErrorConstants.ERRORDOMAIN, new Object[] {filename, se.toString()}), se); } catch (IOException ioe) { throw new ServiceCreationException(ErrorDataFactory.createErrorData(ErrorConstants.CFG_IO_ERROR, ErrorConstants.ERRORDOMAIN, new Object[] {schemaName, ioe.toString()}), ioe); } } private static class ParseErrorHandler implements ErrorHandler { private final String m_filename; private final SchemaValidationLevel m_checkLevel; ParseErrorHandler(String filename, SchemaValidationLevel checkLevel) { m_filename = filename; m_checkLevel = checkLevel; } public void warning(SAXParseException e) { reportError(Level.WARNING, e); } public void error(SAXParseException e) throws SAXParseException { reportError(Level.SEVERE, e); if (m_checkLevel.equals(SchemaValidationLevel.ERROR)) { throw e; } } public void fatalError(SAXParseException e) throws SAXParseException { reportError(Level.SEVERE, e); if (m_checkLevel.equals(SchemaValidationLevel.ERROR)) { throw e; } } private void reportError(Level level, SAXParseException e) { StringBuffer b = new StringBuffer(); b.append(m_filename); int line = e.getLineNumber(); if (line != -1) { b.append(" line "); b.append(line); } b.append(": "); b.append(e.getMessage()); LogManager.getInstance(this.getClass()).log(level, b.toString()); } } }