/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. *******************************************************************************/ package org.apache.ofbiz.base.config; import java.io.InputStream; import java.net.URL; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.UtilURL; import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.base.util.cache.UtilCache; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Loads resources using dynamically specified resource loader classes. */ public abstract class ResourceLoader { public static final String module = ResourceLoader.class.getName(); private static final UtilCache<String, ResourceLoader> loaderCache = UtilCache.createUtilCache("resource.ResourceLoaders", 0, 0); // This cache is temporary - we will use it until the framework has been refactored to eliminate DOM tree caching, then it can be removed. private static final UtilCache<String, Document> domCache = UtilCache.createUtilCache("resource.DomTrees", 0, 0); public static InputStream loadResource(String xmlFilename, String location, String loaderName) throws GenericConfigException { ResourceLoader loader = getLoader(xmlFilename, loaderName); if (loader == null) { throw new IllegalArgumentException("ResourceLoader not found with name [" + loaderName + "] in " + xmlFilename); } return loader.loadResource(location); } public static URL getURL(String xmlFilename, String location, String loaderName) throws GenericConfigException { ResourceLoader loader = getLoader(xmlFilename, loaderName); if (loader == null) { throw new IllegalArgumentException("ResourceLoader not found with name [" + loaderName + "] in " + xmlFilename); } return loader.getURL(location); } public static ResourceLoader getLoader(String xmlFilename, String loaderName) throws GenericConfigException { String cacheKey = xmlFilename.concat("#").concat(loaderName); ResourceLoader loader = loaderCache.get(cacheKey); if (loader == null) { Element rootElement = null; URL xmlUrl = UtilURL.fromResource(xmlFilename); if (xmlUrl == null) { throw new GenericConfigException("Could not find the " + xmlFilename + " file"); } try { rootElement = UtilXml.readXmlDocument(xmlUrl, true, true).getDocumentElement(); } catch (Exception e) { throw new GenericConfigException("Exception thrown while reading " + xmlFilename + ": ", e); } Element loaderElement = UtilXml.firstChildElement(rootElement, "resource-loader", "name", loaderName); if (loaderElement == null) { throw new GenericConfigException("The " + xmlFilename + " file is missing the <resource-loader> element with the name " + loaderName); } if (loaderElement.getAttribute("class").isEmpty()) { throw new GenericConfigException("The " + xmlFilename + " file <resource-loader> element with the name " + loaderName + " is missing the class attribute"); } loader = loaderCache.putIfAbsentAndGet(cacheKey, makeLoader(loaderElement)); } return loader; } /** This method should be avoided. DOM object trees take a lot of memory and they are not * thread-safe, so they should not be cached. * @deprecated use {@link #readXmlRootElement(String)} */ @Deprecated public static Element getXmlRootElement(String xmlFilename) throws GenericConfigException { Document document = ResourceLoader.getXmlDocument(xmlFilename); if (document != null) { return document.getDocumentElement(); } else { return null; } } public static Element readXmlRootElement(String xmlFilename) throws GenericConfigException { Document document = ResourceLoader.readXmlDocument(xmlFilename); if (document != null) { return document.getDocumentElement(); } else { return null; } } public static void invalidateDocument(String xmlFilename) throws GenericConfigException { UtilCache.clearCachesThatStartWith(xmlFilename); } /** This method should be avoided. DOM object trees take a lot of memory and they are not * thread-safe, so they should not be cached. * @deprecated use {@link #readXmlDocument(String)} */ @Deprecated public static Document getXmlDocument(String xmlFilename) throws GenericConfigException { Document document = domCache.get(xmlFilename); if (document == null) { document = readXmlDocument(xmlFilename); if (document != null) { document = (Document) domCache.putIfAbsentAndGet(xmlFilename, document); } } return document; } public static Document readXmlDocument(String xmlFilename) throws GenericConfigException { URL confUrl = UtilURL.fromResource(xmlFilename); if (confUrl == null) { throw new GenericConfigException("ERROR: could not find the [" + xmlFilename + "] XML file on the classpath"); } try { return UtilXml.readXmlDocument(confUrl, true, true); } catch (org.xml.sax.SAXException e) { throw new GenericConfigException("Error reading " + xmlFilename + "", e); } catch (javax.xml.parsers.ParserConfigurationException e) { throw new GenericConfigException("Error reading " + xmlFilename + "", e); } catch (java.io.IOException e) { throw new GenericConfigException("Error reading " + xmlFilename + "", e); } } private static ResourceLoader makeLoader(Element loaderElement) throws GenericConfigException { String loaderName = loaderElement.getAttribute("name"); String className = loaderElement.getAttribute("class"); ResourceLoader loader = null; try { Class<?> lClass = null; ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); lClass = classLoader.loadClass(className); loader = (ResourceLoader) lClass.newInstance(); loader.init(loaderName, loaderElement.getAttribute("prefix"), loaderElement.getAttribute("prepend-env")); return loader; } catch (Exception e) { throw new GenericConfigException("Exception thrown while loading ResourceLoader class \"" + className + "\" ", e); } } private String prefix; private String envName; protected ResourceLoader() {} private void init(String name, String prefix, String envName) { this.prefix = prefix; this.envName = envName; } /** * Just a utility method to be used in loadResource by the implementing class. * @param location * @return the built-up full location */ public String fullLocation(String location) { StringBuilder buf = new StringBuilder(); if (!envName.isEmpty()) { String propValue = System.getProperty(envName); if (propValue == null) { String errMsg = "The Java environment (-Dxxx=yyy) variable with name " + envName + " is not set, cannot load resource."; Debug.logError(errMsg, module); throw new IllegalArgumentException(errMsg); } buf.append(propValue); } buf.append(prefix); buf.append(location); return buf.toString(); } public abstract InputStream loadResource(String location) throws GenericConfigException; public abstract URL getURL(String location) throws GenericConfigException; }