Save This Page
Home » sitemesh-2.3 » com.opensymphony.module » sitemesh » factory » [javadoc | source]
    1   /*
    2    * Title:        DefaultFactory
    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.factory;
   11   
   12   import com.opensymphony.module.sitemesh.Config;
   13   import com.opensymphony.module.sitemesh.DecoratorMapper;
   14   import com.opensymphony.module.sitemesh.PageParser;
   15   
   16   import org.w3c.dom.Document;
   17   import org.w3c.dom.Element;
   18   import org.w3c.dom.NodeList;
   19   import org.w3c.dom.Text;
   20   import org.xml.sax.SAXException;
   21   
   22   import javax.xml.parsers.DocumentBuilder;
   23   import javax.xml.parsers.DocumentBuilderFactory;
   24   import javax.xml.parsers.ParserConfigurationException;
   25   import java.io.File;
   26   import java.io.IOException;
   27   import java.io.InputStream;
   28   import java.util;
   29   
   30   /**
   31    * DefaultFactory, reads configuration from <code>/WEB-INF/sitemesh.xml</code>, or uses the
   32    * default configuration if <code>sitemesh.xml</code> does not exist.
   33    *
   34    * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
   35    * @author <a href="mailto:pathos@pandora.be">Mathias Bogaert</a>
   36    * @version $Revision: 1.3 $
   37    */
   38   public class DefaultFactory extends BaseFactory {
   39       String configFileName = "/WEB-INF/sitemesh.xml";
   40       File configFile;
   41       long configLastModified;
   42       Map configProps = new HashMap();
   43   
   44       String excludesFileName;
   45       File excludesFile;
   46   
   47       public DefaultFactory(Config config) {
   48           super(config);
   49   
   50           // configFilePath is null if loaded from war file
   51           String configFilePath = config.getServletContext().getRealPath(configFileName);
   52   
   53           if (configFilePath != null) { // disable config auto reloading for .war files
   54               configFile = new File(configFilePath);
   55           }
   56   
   57           loadConfig();
   58       }
   59   
   60       /** Refresh config before delegating to superclass. */
   61       public DecoratorMapper getDecoratorMapper() {
   62           refresh();
   63           return super.getDecoratorMapper();
   64       }
   65   
   66       /** Refresh config before delegating to superclass. */
   67       public PageParser getPageParser(String contentType) {
   68           refresh();
   69           return super.getPageParser(contentType);
   70       }
   71   
   72       /** Refresh config before delegating to superclass. */
   73       public boolean shouldParsePage(String contentType) {
   74           refresh();
   75           return super.shouldParsePage(contentType);
   76       }
   77   
   78       /**
   79        * Returns <code>true</code> if the supplied path matches one of the exclude
   80        * URLs specified in sitemesh.xml, otherwise returns <code>false</code>. This
   81        * method refreshes the config before delgating to the superclass.
   82        */
   83       public boolean isPathExcluded(String path) {
   84           refresh();
   85           return super.isPathExcluded(path);
   86       }
   87   
   88       /** Load configuration from file. */
   89       private synchronized void loadConfig() {
   90           try {
   91               // Load and parse the sitemesh.xml file
   92               Element root = loadSitemeshXML();
   93   
   94               NodeList sections = root.getChildNodes();
   95               // Loop through child elements of root node
   96               for (int i = 0; i < sections.getLength(); i++) {
   97                   if (sections.item(i) instanceof Element) {
   98                       Element curr = (Element)sections.item(i);
   99                       NodeList children = curr.getChildNodes();
  100   
  101                       if ("property".equalsIgnoreCase(curr.getTagName())) {
  102                           String name = curr.getAttribute("name");
  103                           String value = curr.getAttribute("value");
  104                           if (!"".equals(name) && !"".equals(value)) {
  105                               configProps.put("${" + name + "}", value);
  106                           }
  107                       }
  108                       else if ("page-parsers".equalsIgnoreCase(curr.getTagName())) {
  109                           // handle <page-parsers>
  110                           loadPageParsers(children);
  111                       }
  112                       else if ("decorator-mappers".equalsIgnoreCase(curr.getTagName())) {
  113                           // handle <decorator-mappers>
  114                           loadDecoratorMappers(children);
  115                       }
  116                       else if ("excludes".equalsIgnoreCase(curr.getTagName())) {
  117                           // handle <excludes>
  118                           String fileName = replaceProperties(curr.getAttribute("file"));
  119                           if (!"".equals(fileName)) {
  120                               excludesFileName = fileName;
  121                               loadExcludes();
  122                           }
  123                       }
  124                   }
  125               }
  126           }
  127           catch (ParserConfigurationException e) {
  128               report("Could not get XML parser", e);
  129           }
  130           catch (IOException e) {
  131               report("Could not read config file : " + configFileName, e);
  132           }
  133           catch (SAXException e) {
  134               report("Could not parse config file : " + configFileName, e);
  135           }
  136       }
  137   
  138       private Element loadSitemeshXML()
  139               throws ParserConfigurationException, IOException, SAXException
  140       {
  141           DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  142           DocumentBuilder builder = factory.newDocumentBuilder();
  143   
  144           InputStream is = null;
  145   
  146           if (configFile == null) {
  147               is = config.getServletContext().getResourceAsStream(configFileName);
  148           }
  149           else if (configFile.exists() && configFile.canRead()) {
  150               is = configFile.toURL().openStream();
  151           }
  152   
  153           if (is == null){ // load the default sitemesh configuration
  154               is = getClass().getClassLoader().getResourceAsStream("com/opensymphony/module/sitemesh/factory/sitemesh-default.xml");
  155           }
  156   
  157           if (is == null){ // load the default sitemesh configuration using another classloader
  158               is = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/opensymphony/module/sitemesh/factory/sitemesh-default.xml");
  159           }
  160   
  161           if (is == null){
  162               throw new IllegalStateException("Cannot load default configuration from jar");
  163           }
  164   
  165           if (configFile != null) configLastModified = configFile.lastModified();
  166   
  167           Document doc = builder.parse(is);
  168           Element root = doc.getDocumentElement();
  169           // Verify root element
  170           if (!"sitemesh".equalsIgnoreCase(root.getTagName())) {
  171               report("Root element of sitemesh configuration file not <sitemesh>", null);
  172           }
  173           return root;
  174       }
  175   
  176       private void loadExcludes()
  177               throws ParserConfigurationException, IOException, SAXException
  178       {
  179           DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  180           DocumentBuilder builder = factory.newDocumentBuilder();
  181   
  182           InputStream is = null;
  183   
  184           if (excludesFile == null) {
  185               is = config.getServletContext().getResourceAsStream(excludesFileName);
  186           }
  187           else if (excludesFile.exists() && excludesFile.canRead()) {
  188               is = excludesFile.toURL().openStream();
  189           }
  190   
  191           if (is == null){
  192               throw new IllegalStateException("Cannot load excludes configuration file from jar");
  193           }
  194   
  195           Document document = builder.parse(is);
  196           Element root = document.getDocumentElement();
  197           NodeList sections = root.getChildNodes();
  198   
  199           // Loop through child elements of root node looking for the <excludes> block
  200           for (int i = 0; i < sections.getLength(); i++) {
  201               if (sections.item(i) instanceof Element) {
  202                   Element curr = (Element)sections.item(i);
  203                   if ("excludes".equalsIgnoreCase(curr.getTagName())) {
  204                       loadExcludeUrls(curr.getChildNodes());
  205                   }
  206               }
  207           }
  208       }
  209   
  210       /** Loop through children of 'page-parsers' element and add all 'parser' mappings. */
  211       private void loadPageParsers(NodeList nodes) {
  212           clearParserMappings();
  213           for (int i = 0; i < nodes.getLength(); i++) {
  214               if (nodes.item(i) instanceof Element) {
  215                   Element curr = (Element)nodes.item(i);
  216   
  217                   if ("parser".equalsIgnoreCase(curr.getTagName())) {
  218                       String className = curr.getAttribute("class");
  219                       String contentType = curr.getAttribute("content-type");
  220                       mapParser(contentType, className);
  221                   }
  222               }
  223           }
  224       }
  225   
  226       private void loadDecoratorMappers(NodeList nodes) {
  227           clearDecoratorMappers();
  228           Properties emptyProps = new Properties();
  229   
  230           pushDecoratorMapper("com.opensymphony.module.sitemesh.mapper.NullDecoratorMapper", emptyProps);
  231   
  232           // note, this works from the bottom node up.
  233           for (int i = nodes.getLength() - 1; i > 0; i--) {
  234               if (nodes.item(i) instanceof Element) {
  235                   Element curr = (Element)nodes.item(i);
  236                   if ("mapper".equalsIgnoreCase(curr.getTagName())) {
  237                       String className = curr.getAttribute("class");
  238                       Properties props = new Properties();
  239                       // build properties from <param> tags.
  240                       NodeList children = curr.getChildNodes();
  241                       for (int j = 0; j < children.getLength(); j++) {
  242                           if (children.item(j) instanceof Element) {
  243                               Element currC = (Element)children.item(j);
  244                               if ("param".equalsIgnoreCase(currC.getTagName())) {
  245                                   String value = currC.getAttribute("value");
  246                                   props.put(currC.getAttribute("name"), replaceProperties(value));
  247                               }
  248                           }
  249                       }
  250                       // add mapper
  251                       pushDecoratorMapper(className, props);
  252                   }
  253               }
  254           }
  255   
  256           pushDecoratorMapper("com.opensymphony.module.sitemesh.mapper.InlineDecoratorMapper", emptyProps);
  257       }
  258   
  259       /**
  260        * Reads in all the url patterns to exclude from decoration.
  261        */
  262       private void loadExcludeUrls(NodeList nodes) {
  263           clearExcludeUrls();
  264           for (int i = 0; i < nodes.getLength(); i++) {
  265               if (nodes.item(i) instanceof Element) {
  266                   Element p = (Element) nodes.item(i);
  267                   if ("pattern".equalsIgnoreCase(p.getTagName()) || "url-pattern".equalsIgnoreCase(p.getTagName())) {
  268                       Text patternText = (Text) p.getFirstChild();
  269                       if (patternText != null) {
  270                           String pattern = patternText.getData().trim();
  271                           if (pattern != null) {
  272                               addExcludeUrl(pattern);
  273                           }
  274                       }
  275                   }
  276               }
  277           }
  278       }
  279   
  280       /** Check if configuration file has been modified, and if so reload it. */
  281       private void refresh() {
  282           if (configFile != null && configLastModified != configFile.lastModified()) loadConfig();
  283       }
  284   
  285       /**
  286        * Replaces any properties that appear in the supplied string
  287        * with their actual values
  288        *
  289        * @param str the string to replace the properties in
  290        * @return the same string but with any properties expanded out to their
  291        * actual values
  292        */
  293       private String replaceProperties(String str) {
  294           Set props = configProps.entrySet();
  295           for (Iterator it = props.iterator(); it.hasNext();)
  296           {
  297               Map.Entry entry = (Map.Entry) it.next();
  298               String key = (String) entry.getKey();
  299               int idx;
  300               while ((idx = str.indexOf(key)) >= 0) {
  301                   StringBuffer buf = new StringBuffer(100);
  302                   buf.append(str.substring(0, idx));
  303                   buf.append(entry.getValue());
  304                   buf.append(str.substring(idx + key.length()));
  305                   str = buf.toString();
  306               }
  307           }
  308           return str;
  309       }
  310   }

Save This Page
Home » sitemesh-2.3 » com.opensymphony.module » sitemesh » factory » [javadoc | source]