📄 configloader.java
字号:
/* * Title: ConfigLoader * Description: * * This software is published under the terms of the OpenSymphony Software * License version 1.1, of which a copy has been included with this * distribution in the LICENSE.txt file. */package com.opensymphony.module.sitemesh.mapper;import com.opensymphony.module.sitemesh.Config;import com.opensymphony.module.sitemesh.Decorator;import com.opensymphony.module.sitemesh.factory.DefaultFactory;import org.w3c.dom.*;import org.xml.sax.SAXException;import javax.servlet.ServletException;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import java.io.File;import java.io.IOException;import java.util.HashMap;import java.util.Map;/** * The ConfigLoader reads a configuration XML file that contains Decorator definitions * (name, url, init-params) and path-mappings (pattern, name). * * <p>These can then be accessed by the getDecoratorByName() methods and getMappedName() * methods respectively.</p> * * <p>The DTD for the configuration file in old (deprecated) format is located at * <a href="http://www.opensymphony.com/dtds/sitemesh_1_0_decorators.dtd"> * http://www.opensymphony.com/dtds/sitemesh_1_0_decorators.dtd * </a>.</p> * * <p>The DTD for the configuration file in new format is located at * <a href="http://www.opensymphony.com/dtds/sitemesh_1_5_decorators.dtd"> * http://www.opensymphony.com/dtds/sitemesh_1_5_decorators.dtd * </a>.</p> * * <p>Editing the config file will cause it to be auto-reloaded.</p> * * <p>This class is used by ConfigDecoratorMapper, and uses PathMapper for pattern matching.</p> * * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a> * @author <a href="mailto:pathos@pandora.be">Mathias Bogaert</a> * @version $Revision: 1.8 $ * * @see com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper * @see com.opensymphony.module.sitemesh.mapper.PathMapper */public class ConfigLoader { /** * State visibile across threads stored in a single container so that we * can efficiently atomically access it with the guarantee that we wont see * a partially loaded configuration in the face of one thread reloading the * configuration while others are trying to read it. */ private static class State { /** * Timestamp of the last time we checked for an update to the * configuration file used to rate limit the frequency at which we check * for efficiency. */ long lastModificationCheck = System.currentTimeMillis(); /** * Timestamp of the modification time of the configuration file when we * generated the state. */ long lastModified; /** * Whether a thread is currently checking if the configuration file has * been modified and potentially reloading it and therefore others * shouldn't attempt the same till it's done. */ boolean checking = false; Map decorators = new HashMap(); PathMapper pathMapper = new PathMapper(); } /** * Mark volatile so that the installation of new versions is guaranteed to * be visible across threads. */ private volatile State state; private File configFile = null; private String configFileName = null; private Config config = null; /** * Create new ConfigLoader using supplied File. */ public ConfigLoader(File configFile) throws ServletException { this.configFile = configFile; this.configFileName = configFile.getName(); state = loadConfig(); } /** * Create new ConfigLoader using supplied filename and config. */ public ConfigLoader(String configFileName, Config config) throws ServletException { this.config = config; this.configFileName = configFileName; if (config.getServletContext().getRealPath(configFileName) != null) { this.configFile = new File(config.getServletContext().getRealPath(configFileName)); } state = loadConfig(); } /** * Retrieve Decorator based on name specified in configuration file. */ public Decorator getDecoratorByName(String name) throws ServletException { return (Decorator) refresh().decorators.get(name); } /** Get name of Decorator mapped to given path. */ public String getMappedName(String path) throws ServletException { return refresh().pathMapper.get(path); } /** * Load configuration from file. */ private State loadConfig() throws ServletException { // The new state which we build up and atomically replace the old state // with atomically to avoid other threads seeing partial modifications. State newState = new State(); try { // Build a document from the file DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document; if (configFile != null && configFile.canRead()) { // Keep time we read the file to check if the file was modified newState.lastModified = configFile.lastModified(); document = builder.parse(configFile); } else { document = builder.parse(config.getServletContext().getResourceAsStream(configFileName)); } // Parse the configuration document parseConfig(newState, document); return newState; } catch (ParserConfigurationException e) { throw new ServletException("Could not get XML parser", e); } catch (IOException e) { throw new ServletException("Could not read the config file: " + configFileName, e); } catch (SAXException e) { throw new ServletException("Could not parse the config file: " + configFileName, e); } catch (IllegalArgumentException e) { throw new ServletException("Could not find the config file: " + configFileName, e); } } private void parseConfig(State newState, Document document) { Element root = document.getDocumentElement(); // get the default directory for the decorators String defaultDir = getAttribute(root, "defaultdir"); if (defaultDir == null) defaultDir = getAttribute(root, "defaultDir"); // Get decorators NodeList decoratorNodes = root.getElementsByTagName("decorator"); Element decoratorElement; for (int i = 0; i < decoratorNodes.getLength(); i++) { String name, page, uriPath = null, role = null; // get the current decorator element decoratorElement = (Element) decoratorNodes.item(i); if (getAttribute(decoratorElement, "name") != null) { // The new format is used name = getAttribute(decoratorElement, "name"); page = getAttribute(decoratorElement, "page"); uriPath = getAttribute(decoratorElement, "webapp"); role = getAttribute(decoratorElement, "role"); // Append the defaultDir if (defaultDir != null && page != null && page.length() > 0 && !page.startsWith("/")) { if (page.charAt(0) == '/') page = defaultDir + page; else page = defaultDir + '/' + page; } // The uriPath must begin with a slash if (uriPath != null && uriPath.length() > 0) { if (uriPath.charAt(0) != '/') uriPath = '/' + uriPath; } // Get all <pattern>...</pattern> and <url-pattern>...</url-pattern> nodes and add a mapping populatePathMapper(newState, decoratorElement.getElementsByTagName("pattern"), role, name); populatePathMapper(newState, decoratorElement.getElementsByTagName("url-pattern"), role, name); } else { // NOTE: Deprecated format name = getContainedText(decoratorNodes.item(i), "decorator-name"); page = getContainedText(decoratorNodes.item(i), "resource"); // We have this here because the use of jsp-file is deprecated, but we still want // it to work. if (page == null) page = getContainedText(decoratorNodes.item(i), "jsp-file"); } Map params = new HashMap(); NodeList paramNodes = decoratorElement.getElementsByTagName("init-param"); for (int ii = 0; ii < paramNodes.getLength(); ii++) { String paramName = getContainedText(paramNodes.item(ii), "param-name"); String paramValue = getContainedText(paramNodes.item(ii), "param-value"); params.put(paramName, paramValue); } storeDecorator(newState, new DefaultDecorator(name, page, uriPath, role, params)); } // Get (deprecated format) decorator-mappings NodeList mappingNodes = root.getElementsByTagName("decorator-mapping"); for (int i = 0; i < mappingNodes.getLength(); i++) { Element n = (Element) mappingNodes.item(i); String name = getContainedText(mappingNodes.item(i), "decorator-name"); // Get all <url-pattern>...</url-pattern> nodes and add a mapping populatePathMapper(newState, n.getElementsByTagName("url-pattern"), null, name); } } private void populatePathMapper(State newState, NodeList patternNodes, String role, String name) { for (int j = 0; j < patternNodes.getLength(); j++) { Element p = (Element) patternNodes.item(j); Text patternText = (Text) p.getFirstChild(); if (patternText != null) { String pattern = patternText.getData().trim(); if (pattern != null) { if (role != null) { // concatenate name and role to allow more // than one decorator per role newState.pathMapper.put(name + role, pattern); } else { newState.pathMapper.put(name, pattern); } } } } } private static String getAttribute(Element element, String name) { if (element != null && element.getAttribute(name) != null && element.getAttribute(name).trim() != "") { return element.getAttribute(name).trim(); } else { return null; } } private static String getContainedText(Node parent, String childTagName) { try { Node tag = ((Element) parent).getElementsByTagName(childTagName).item(0); return ((Text) tag.getFirstChild()).getData(); } catch (Exception e) { return null; } } private void storeDecorator(State newState, Decorator d) { if (d.getRole() != null) { newState.decorators.put(d.getName() + d.getRole(), d); } else { newState.decorators.put(d.getName(), d); } } /** * Check if configuration file has been updated, and if so, reload. */ private State refresh() throws ServletException { // Read the current state just once since another thread can swap // another version in at any time. State currentState = state; if (configFile == null) { return currentState; } // Rate limit the stat'ing of the config file to find its // modification time to once every five seconds to reduce the // number of system calls made. We grab the monitor of currentState // so that we can safely read the values shared across threads and // so that only one thread is performing the modification check at // a time. long current = System.currentTimeMillis(); long oldLastModified; boolean check = false; synchronized (currentState) { oldLastModified = currentState.lastModified; if (!currentState.checking && current >= currentState.lastModificationCheck + DefaultFactory.configCheckMillis) { currentState.lastModificationCheck = current; currentState.checking = true; check = true; } } if (check) { // Perform the file stat'ing system call without holding a lock // on the current state. State newState = null; try { long currentLastModified = configFile.lastModified(); if (currentLastModified != oldLastModified) { // The configuration file has been modified since we last // read it so reload the configuration without holding a // lock on the current state and then slam down the new // state for other threads to see. The State.checking flag // being set on currentState will prevent other threads // from attempting to reload the state while we are and // the new state will have the flag cleared so that we can // continue checking if the configuration file is modified. newState = loadConfig(); state = newState; return newState; } } finally { // In the event of a failure of the modification time check, // or while reloading the configuration file, mark that we're no // longer checking if the modification time or reloading so that // we'll retry. if (newState == null) { synchronized (currentState) { currentState.checking = false; } } } } return currentState; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -