Home » sitemesh-2.4.1 » com.opensymphony.module » sitemesh » mapper » [javadoc | source]

    1   /*
    2    * Title:        ConfigLoader
    3    * Description:
    4    *
    5    * This software is published under the terms of the OpenSymphony Software
    6    * License version 1.1, of which a copy has been included with this
    7    * distribution in the LICENSE.txt file.
    8    */
    9   
   10   package com.opensymphony.module.sitemesh.mapper;
   11   
   12   import com.opensymphony.module.sitemesh.Config;
   13   import com.opensymphony.module.sitemesh.Decorator;
   14   import org.w3c.dom;
   15   import org.xml.sax.SAXException;
   16   
   17   import javax.servlet.ServletException;
   18   import javax.xml.parsers.DocumentBuilder;
   19   import javax.xml.parsers.DocumentBuilderFactory;
   20   import javax.xml.parsers.ParserConfigurationException;
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.util.HashMap;
   24   import java.util.Map;
   25   
   26   /**
   27    * The ConfigLoader reads a configuration XML file that contains Decorator definitions
   28    * (name, url, init-params) and path-mappings (pattern, name).
   29    *
   30    * <p>These can then be accessed by the getDecoratorByName() methods and getMappedName()
   31    * methods respectively.</p>
   32    *
   33    * <p>The DTD for the configuration file in old (deprecated) format is located at
   34    * <a href="http://www.opensymphony.com/dtds/sitemesh_1_0_decorators.dtd">
   35    *  http://www.opensymphony.com/dtds/sitemesh_1_0_decorators.dtd
   36    * </a>.</p>
   37    *
   38    * <p>The DTD for the configuration file in new format is located at
   39    * <a href="http://www.opensymphony.com/dtds/sitemesh_1_5_decorators.dtd">
   40    *  http://www.opensymphony.com/dtds/sitemesh_1_5_decorators.dtd
   41    * </a>.</p>
   42    *
   43    * <p>Editing the config file will cause it to be auto-reloaded.</p>
   44    *
   45    * <p>This class is used by ConfigDecoratorMapper, and uses PathMapper for pattern matching.</p>
   46    *
   47    * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
   48    * @author <a href="mailto:pathos@pandora.be">Mathias Bogaert</a>
   49    * @version $Revision: 1.6 $
   50    *
   51    * @see com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper
   52    * @see com.opensymphony.module.sitemesh.mapper.PathMapper
   53    */
   54   public final class ConfigLoader {
   55       private Map decorators = null;
   56       private long configLastModified;
   57   
   58       private File configFile = null;
   59       private String configFileName = null;
   60       private PathMapper pathMapper = null;
   61   
   62       private Config config = null;
   63   
   64       /** Create new ConfigLoader using supplied File. */
   65       public ConfigLoader(File configFile) throws ServletException {
   66           this.configFile = configFile;
   67           this.configFileName = configFile.getName();
   68           loadConfig();
   69       }
   70   
   71       /** Create new ConfigLoader using supplied filename and config. */
   72       public ConfigLoader(String configFileName, Config config) throws ServletException {
   73           this.config = config;
   74           this.configFileName = configFileName;
   75           if (config.getServletContext().getRealPath(configFileName) != null) {
   76               this.configFile = new File(config.getServletContext().getRealPath(configFileName));
   77           }
   78           loadConfig();
   79       }
   80   
   81       /** Retrieve Decorator based on name specified in configuration file. */
   82       public Decorator getDecoratorByName(String name) throws ServletException {
   83           refresh();
   84           return (Decorator)decorators.get(name);
   85       }
   86   
   87       /** Get name of Decorator mapped to given path. */
   88       public String getMappedName(String path) throws ServletException {
   89           refresh();
   90           return pathMapper.get(path);
   91       }
   92   
   93       /** Load configuration from file. */
   94       private synchronized void loadConfig() throws ServletException {
   95           try {
   96               // Build a document from the file
   97               DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
   98               DocumentBuilder builder = factory.newDocumentBuilder();
   99   
  100               Document document = null;
  101               if (configFile != null && configFile.canRead()) {
  102                   // Keep time we read the file to check if the file was modified
  103                   configLastModified = configFile.lastModified();
  104                   document = builder.parse(configFile);
  105               }
  106               else {
  107                   document = builder.parse(config.getServletContext().getResourceAsStream(configFileName));
  108               }
  109   
  110               // Parse the configuration document
  111               parseConfig(document);
  112           }
  113           catch (ParserConfigurationException e) {
  114               throw new ServletException("Could not get XML parser", e);
  115           }
  116           catch (IOException e) {
  117               throw new ServletException("Could not read the config file: " + configFileName, e);
  118           }
  119           catch (SAXException e) {
  120               throw new ServletException("Could not parse the config file: " + configFileName, e);
  121           }
  122           catch (IllegalArgumentException e) {
  123               throw new ServletException("Could not find the config file: " + configFileName, e);
  124           }
  125       }
  126   
  127       /** Parse configuration from XML document. */
  128       private synchronized void parseConfig(Document document) {
  129           Element root = document.getDocumentElement();
  130   
  131           // get the default directory for the decorators
  132           String defaultDir = getAttribute(root, "defaultdir");
  133           if (defaultDir == null) defaultDir = getAttribute(root, "defaultDir");
  134   
  135           // Clear previous config
  136           pathMapper = new PathMapper();
  137           decorators = new HashMap();
  138   
  139           // Get decorators
  140           NodeList decoratorNodes = root.getElementsByTagName("decorator");
  141           Element decoratorElement = null;
  142   
  143           for (int i = 0; i < decoratorNodes.getLength(); i++) {
  144               String name = null, page = null, uriPath = null, role = null;
  145   
  146               // get the current decorator element
  147               decoratorElement = (Element) decoratorNodes.item(i);
  148   
  149               if (getAttribute(decoratorElement, "name") != null) {
  150                   // The new format is used
  151                   name = getAttribute(decoratorElement, "name");
  152                   page = getAttribute(decoratorElement, "page");
  153                   uriPath = getAttribute(decoratorElement, "webapp");
  154                   role = getAttribute(decoratorElement, "role");
  155   
  156                   // Append the defaultDir
  157                   if (defaultDir != null && page != null && page.length() > 0 && !page.startsWith("/")) {
  158                       if (page.charAt(0) == '/') page = defaultDir + page;
  159                       else                       page = defaultDir + '/' + page;
  160                   }
  161   
  162                   // The uriPath must begin with a slash
  163                   if (uriPath != null && uriPath.length() > 0) {
  164                       if (uriPath.charAt(0) != '/') uriPath = '/' + uriPath;
  165                   }
  166   
  167                   // Get all <pattern>...</pattern> and <url-pattern>...</url-pattern> nodes and add a mapping
  168                  populatePathMapper(decoratorElement.getElementsByTagName("pattern"), role, name);
  169                  populatePathMapper(decoratorElement.getElementsByTagName("url-pattern"), role, name);
  170               }
  171               else {
  172                   // NOTE: Deprecated format
  173                   name = getContainedText(decoratorNodes.item(i), "decorator-name");
  174                   page = getContainedText(decoratorNodes.item(i), "resource");
  175                   // We have this here because the use of jsp-file is deprecated, but we still want
  176                   // it to work.
  177                   if (page == null) page = getContainedText(decoratorNodes.item(i), "jsp-file");
  178               }
  179   
  180               Map params = new HashMap();
  181   
  182               NodeList paramNodes = decoratorElement.getElementsByTagName("init-param");
  183               for (int ii = 0; ii < paramNodes.getLength(); ii++) {
  184                   String paramName = getContainedText(paramNodes.item(ii), "param-name");
  185                   String paramValue = getContainedText(paramNodes.item(ii), "param-value");
  186                   params.put(paramName, paramValue);
  187               }
  188               storeDecorator(new DefaultDecorator(name, page, uriPath, role, params));
  189           }
  190   
  191           // Get (deprecated format) decorator-mappings
  192           NodeList mappingNodes = root.getElementsByTagName("decorator-mapping");
  193           for (int i = 0; i < mappingNodes.getLength(); i++) {
  194               Element n = (Element)mappingNodes.item(i);
  195               String name = getContainedText(mappingNodes.item(i), "decorator-name");
  196   
  197               // Get all <url-pattern>...</url-pattern> nodes and add a mapping
  198               populatePathMapper(n.getElementsByTagName("url-pattern"), null, name);
  199           }
  200       }
  201   
  202      /**
  203       * Extracts each URL pattern and adds it to the pathMapper map.
  204       */
  205      private void populatePathMapper(NodeList patternNodes, String role, String name) {
  206         for (int j = 0; j < patternNodes.getLength(); j++) {
  207             Element p = (Element)patternNodes.item(j);
  208             Text patternText = (Text) p.getFirstChild();
  209             if (patternText != null) {
  210                String pattern = patternText.getData().trim();
  211                if (pattern != null) {
  212                    if (role != null) {
  213                        // concatenate name and role to allow more
  214                        // than one decorator per role
  215                        pathMapper.put(name + role, pattern);
  216                    }
  217                    else {
  218                        pathMapper.put(name, pattern);
  219                    }
  220                }
  221            }
  222         }
  223      }
  224   
  225      /** Override default behavior of element.getAttribute (returns the empty string) to return null. */
  226       private static String getAttribute(Element element, String name) {
  227           if (element != null && element.getAttribute(name) != null && element.getAttribute(name).trim() != "") {
  228               return element.getAttribute(name).trim();
  229           }
  230           else {
  231               return null;
  232           }
  233       }
  234   
  235       /**
  236        * With a given parent XML Element, find the text contents of the child element with
  237        * supplied name.
  238        */
  239       private static String getContainedText(Node parent, String childTagName) {
  240           try {
  241               Node tag = ((Element)parent).getElementsByTagName(childTagName).item(0);
  242               String text = ((Text)tag.getFirstChild()).getData();
  243               return text;
  244           }
  245           catch (Exception e) {
  246               return null;
  247           }
  248       }
  249   
  250       /** Store Decorator in Map */
  251       private void storeDecorator(Decorator d) {
  252           if (d.getRole() != null) {
  253               decorators.put(d.getName() + d.getRole(), d);
  254           }
  255           else {
  256               decorators.put(d.getName(), d);
  257           }
  258       }
  259   
  260       /** Check if configuration file has been updated, and if so, reload. */
  261       private synchronized void refresh() throws ServletException {
  262           if (configFile != null && configLastModified != configFile.lastModified()) loadConfig();
  263       }
  264   }

Home » sitemesh-2.4.1 » com.opensymphony.module » sitemesh » mapper » [javadoc | source]