2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.clay.config.beans.ComponentConfigBean
 
Classes in this File Line Coverage Branch Coverage Complexity
ComponentConfigBean
100%
403/403
N/A
4.769
ComponentConfigBean$WatchDog
100%
46/46
N/A
4.769
ComponentConfigBean$XmlConfigDef
100%
9/9
N/A
4.769
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to you under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 /*
 19  
  * $Id: ComponentConfigBean.java 467369 2006-10-24 16:04:31Z gvanmatre $
 20  
  */
 21  
 package org.apache.shale.clay.config.beans;
 22  
 
 23  
 import java.io.IOException;
 24  
 import java.net.URL;
 25  
 import java.net.URLConnection;
 26  
 import java.util.ArrayList;
 27  
 import java.util.Enumeration;
 28  
 import java.util.HashMap;
 29  
 import java.util.Iterator;
 30  
 import java.util.List;
 31  
 import java.util.Map;
 32  
 import java.util.Stack;
 33  
 import java.util.StringTokenizer;
 34  
 import java.util.TreeMap;
 35  
 import java.util.TreeSet;
 36  
 
 37  
 import javax.servlet.ServletContext;
 38  
 
 39  
 import org.apache.commons.logging.Log;
 40  
 import org.apache.commons.logging.LogFactory;
 41  
 import org.apache.shale.clay.config.ClayConfigParser;
 42  
 import org.apache.shale.clay.config.ClayXmlParser;
 43  
 import org.apache.shale.clay.config.Globals;
 44  
 import org.apache.shale.util.Messages;
 45  
 import org.xml.sax.SAXException;
 46  
 
 47  
 /**
 48  
  * <p>This class is kind of the metadata object pool for configuration data
 49  
  *  loaded from XML files on startup in the {@link org.apache.shale.clay.config.ClayConfigureListener}
 50  
  *  by the {@link org.apache.shale.clay.config.ClayXmlParser}.  An instance of this
 51  
  *  class will be registered with the {@link ConfigBeanFactory}.
 52  
  *  </p>
 53  
  */
 54  174
 public class ComponentConfigBean implements ConfigBean {
 55  
 
 56  
     /**
 57  
      * <p>Commons logger.</p>
 58  
      */
 59  
     private static Log log;
 60  
     static {
 61  2
         log = LogFactory.getLog(ComponentConfigBean.class);
 62  
     }
 63  
 
 64  
     /**
 65  
      * <p>Uses the digester to load the configuration files
 66  
      * into a object graph cached in <code>displayElements</code>.
 67  
      * </p>
 68  
      */
 69  144
     protected ClayConfigParser parser = null;
 70  
 
 71  
     /**
 72  
      * <p>This parameter is initialized from the <code>init</code>
 73  
      * method from the <code>org.apache.shale.clay.AUTO_RELOAD_CONFIG_FILES</code> init
 74  
      * parameter in the web.xml.  The default value is <code>true</code>
 75  
      * which will trigger reloading the files when a change has occurred.
 76  
      * </p>
 77  
      */
 78  144
     protected boolean isWatchDogOn = true;
 79  
 
 80  
 
 81  
     /**
 82  
      * <p>Map of {@link WatchDog} that watches the configuration files looking for changes.
 83  
      * The configuration files are defined by the {@link ConfigBean.ConfigDefinition} top level
 84  
      * interface.</p>
 85  
      */
 86  144
     protected Map watchDogs = null;
 87  
 
 88  
     /**
 89  
      * <p>
 90  
      * Message resources for this class.
 91  
      * </p>
 92  
      */
 93  1
     protected static Messages messages = new Messages(
 94  
             "org.apache.shale.clay.Bundle", ComponentConfigBean.class
 95  
             .getClassLoader());
 96  
 
 97  
     /**
 98  
      *  <p>The suffixes used to identify that a jsfid is a template style of
 99  
      *  composition.  If it has a matching suffix, it will be handled
 100  
      *  by the {@link TemplateConfigBean} or {@link TemplateComponentConfigBean};
 101  
      *  Otherwise, it's handled by {@link ComponentConfigBean}.</p>
 102  
      */
 103  144
     protected String[] suffixes = null;
 104  
 
 105  
     /**
 106  
      * <p>Reference to the <code>ServletContext</code>.</p>
 107  
      */
 108  144
     protected transient ServletContext context = null;
 109  
 
 110  
 
 111  
     /**
 112  
      * <p>Flag that indicates the current mode is design time.
 113  
      * In design time mode, the descriptions in the clay
 114  
      * configuration files will populate the <code>description</code>
 115  
      * property of the target {@link AbstractBean}.</p>
 116  
      */
 117  144
     private boolean isDesigntime = false;
 118  
 
 119  
     /**
 120  
      * <p>Returns <code>true</code> if the current mode
 121  
      * is design time.</p>
 122  
      *
 123  
      * @return <code>true</code> if design time
 124  
      */
 125  
     public boolean isDesigntime() {
 126  42
        return isDesigntime;
 127  
     }
 128  
 
 129  
     /**
 130  
      * <p>Sets the design time to somthing other than
 131  
      * the default <code>false</code> value.</p>
 132  
      *
 133  
      * @param isDesigntime load config descriptions
 134  
      */
 135  
     public void setDesigntime(boolean isDesigntime) {
 136  4
        this.isDesigntime = isDesigntime;
 137  4
     }
 138  
 
 139  
 
 140  
     /**
 141  
      * <p>Initialization method that is passed the <code>ServletContext</code>
 142  
      * as a parameter.  Loads the <code>sufixes</code> for the ServletContext
 143  
      * initialization parameter.
 144  
      * </p>
 145  
      *
 146  
      * @param context servlet context
 147  
      */
 148  
     public  void init(ServletContext context) {
 149  120
         this.context = context;
 150  
 
 151  120
         if (suffixes == null) {
 152  120
             suffixes = new String[2];
 153  
 
 154  120
             suffixes[0] = context.getInitParameter(
 155  
                     Globals.CLAY_HTML_TEMPLATE_SUFFIX);
 156  120
             if (suffixes[0] == null) {
 157  
                 suffixes[0] = Globals.CLAY_DEFAULT_HTML_TEMPLATE_SUFFIX;
 158  
             }
 159  
 
 160  120
             suffixes[1] = context.getInitParameter(
 161  
                     Globals.CLAY_XML_TEMPLATE_SUFFIX);
 162  120
             if (suffixes[1] == null) {
 163  
                 suffixes[1] = Globals.CLAY_DEFAULT_XML_TEMPLATE_SUFFIX;
 164  
             }
 165  
 
 166  
         }
 167  
 
 168  120
         String autoReloadClayFiles = context.getInitParameter(Globals.AUTO_RELOAD_CLAY_FILES);
 169  120
         if (autoReloadClayFiles != null) {
 170  
             try {
 171  
                 isWatchDogOn = Boolean.valueOf(autoReloadClayFiles).booleanValue();
 172  
             } catch (RuntimeException e) {
 173  
                 isWatchDogOn = false;
 174  
             }
 175  
 
 176  
         }
 177  
 
 178  
         //loads the config files
 179  120
         loadConfigFiles();
 180  120
     }
 181  
 
 182  
 
 183  
     /**
 184  
      * <p>Loads the {@link org.apache.shale.clay.component.Clay} configration files
 185  
      * into the <code>displayElements</code> Map.  The files are defined by the
 186  
      * <code>clay-template-suffix</code> initialization parameter in the web deployment
 187  
      * descriptor.  The default configuration file "META-INF/view-config.xml" is always
 188  
      * loaded from the shale-clay java archive.</p>
 189  
      */
 190  
     protected void loadConfigFiles() {
 191  
 
 192  40
         parser = new ClayXmlParser();
 193  40
         parser.setConfig(this);
 194  
 
 195  
         // grab the default config file
 196  40
         StringBuffer configFiles = new StringBuffer(
 197  
                 Globals.DEFAULT_CLAY_CONFIG_FILE);
 198  
 
 199  
         // a comma delimited value list of config files
 200  40
         String param = context.getInitParameter(Globals.CLAY_CONFIG_FILES);
 201  
 
 202  
         // add the default config file
 203  40
         if (param != null && param.trim().length() > 0) {
 204  
             configFiles.append(", ").append(param);
 205  
 
 206  
             log.warn(messages.getMessage("config.deprecated.param",
 207  
                      new Object[] {Globals.CLAY_CONFIG_FILES,
 208  
                                    Globals.CLAY_COMMON_CONFIG_FILES}));
 209  
         }
 210  
 
 211  
         // a comma delimited value list of config files
 212  40
         param = context.getInitParameter(Globals.CLAY_COMMON_CONFIG_FILES);
 213  40
         if (param != null && param.trim().length() > 0) {
 214  13
             configFiles.append(", ").append(param);
 215  
         }
 216  
 
 217  
         // pass the config bean to the parser
 218  40
         parser.setConfig(this);
 219  
 
 220  
         // map holding the resource watchers
 221  40
         watchDogs = new TreeMap();
 222  
 
 223  
         // create the watch dog with a list of config files to look for changes
 224  40
         WatchDog watchDog = new WatchDog(getConfigDefinitions(configFiles.toString()),
 225  
                 Globals.DEFAULT_COMPONENT_CONFIG_WATCHDOG);
 226  
 
 227  
         // adds the watcher to a map identified by name
 228  40
         watchDogs.put(watchDog.getName(), watchDog);
 229  
 
 230  
         // loads the config files
 231  40
         watchDog.refresh(true);
 232  
 
 233  40
         param = null;
 234  40
         configFiles = null;
 235  
 
 236  40
     }
 237  
 
 238  
     /**
 239  
      * <p>Passed a comma delimited list of configuration files, this method returns
 240  
      * an array of {@link ConfigBean.ConfigDefinition} defining the files.</p>
 241  
      *
 242  
      * @param configFiles comma seperated list of config files
 243  
      * @return config definitions for the files
 244  
      */
 245  
     protected ConfigBean.ConfigDefinition[] getConfigDefinitions(String configFiles) {
 246  
 
 247  74
         List urls = new ArrayList();
 248  
 
 249  74
         ClassLoader classloader = Thread.currentThread().getContextClassLoader();
 250  74
         if (classloader == null) {
 251  
             classloader = this.getClass().getClassLoader();
 252  
         }
 253  
 
 254  
         // convert a tokenized list of configuration files into an array of urls
 255  74
         StringTokenizer tokenizer = new StringTokenizer(configFiles, ", ");
 256  161
         while (tokenizer.hasMoreTokens()) {
 257  87
             StringBuffer configFile = new StringBuffer(tokenizer.nextToken().trim());
 258  
 
 259  
             //look for a classpath prefix.
 260  87
             int i = configFile.indexOf(Globals.CLASSPATH_PREFIX);
 261  87
             if (i > -1) {
 262  40
                configFile.delete(0, i + Globals.CLASSPATH_PREFIX.length());
 263  
             }
 264  
 
 265  
             try {
 266  87
                 if (i > -1) {
 267  40
                    for (Enumeration ui = classloader.getResources(configFile.toString());
 268  80
                          ui.hasMoreElements();) {
 269  40
                        urls.add(ui.nextElement());
 270  40
                    }
 271  40
                 } else {
 272  47
                    URL url = context.getResource(configFile.toString());
 273  47
                    if (url == null) {
 274  
                       throw new PageNotFoundException(messages.getMessage("file.notfound",
 275  
                               new Object[] {configFile.toString()}), configFile.toString());
 276  
                    }
 277  47
                    urls.add(url);
 278  
                 }
 279  
             } catch (IOException e) {
 280  
                 log.error(e);
 281  87
             }
 282  
 
 283  87
             configFile = null;
 284  87
         }
 285  74
         tokenizer = null;
 286  74
         classloader = null;
 287  
 
 288  74
         ConfigBean.ConfigDefinition[] configDefs = new ConfigBean.ConfigDefinition[urls.size()];
 289  
 
 290  161
         for (int i = 0; i < urls.size(); i++) {
 291  87
             configDefs[i] = new XmlConfigDef((URL) urls.get(i));
 292  
         }
 293  
 
 294  74
         return configDefs;
 295  
     }
 296  
 
 297  
     /**
 298  
      * <p>Returns the web container ServletContext.</p>
 299  
      *
 300  
      * @return servlet context
 301  
      */
 302  
     public ServletContext getServletContext() {
 303  18
         return context;
 304  
     }
 305  
 
 306  
     /**
 307  
      * <p>Collection holding all the top-level components defined in the XML
 308  
      * config files.</p>
 309  
      */
 310  144
     protected Map displayElements = null;
 311  
 
 312  
     /**
 313  
      * <p>Constructor initializes the <code>displayElements</code>
 314  
      * collection.</p>
 315  
      */
 316  144
     public ComponentConfigBean() {
 317  144
         final int size = 1000;
 318  144
         displayElements = new HashMap(size);
 319  144
     }
 320  
 
 321  
     /**
 322  
      * <p>Factory method that returns a top-level {link ComponentBean} with a
 323  
      *  matching <code>jsfid</code> or <code>null</code> if not found.
 324  
      * </p>
 325  
      *
 326  
      * @param jsfid id of component definition
 327  
      * @return component definition for the jsfid
 328  
      */
 329  
     public ComponentBean getElement(String jsfid) {
 330  722
         ComponentBean element = null;
 331  722
         synchronized (displayElements) {
 332  722
             element = (ComponentBean) displayElements.get(jsfid);
 333  722
         }
 334  722
         return element;
 335  
     }
 336  
 
 337  
     /**
 338  
      * <p>Adds a {link ComponentBean} to the <code>displayElement</code> map collection using
 339  
      * the <code>jsfid</code> as the key.</p>
 340  
      *
 341  
      * @param obj component bean added to the map of elements
 342  
      */
 343  
     public void addChild(ComponentBean obj) {
 344  7441
         if (obj.getJsfid() != null) {
 345  7441
             displayElements.put(obj.getJsfid(), obj);
 346  7441
         } else {
 347  
             log.error(messages.getMessage("missing.jsfid.error",
 348  
                     new Object[] {"ComponentBean.jsfid", "ComponentConfigBean"}));
 349  
         }
 350  
 
 351  7441
     }
 352  
 
 353  
     /**
 354  
      * <p>This method is called on startup to resolve the meta inheritance relationships for
 355  
      * each top-level components in the <code>displayElements</code> collection.  There are
 356  
      * three steps, find parents, check for circular relationships, and realize the relationships.
 357  
      * </p>
 358  
      */
 359  
     public void resolveInheritance() {
 360  
 
 361  73
         if (log.isInfoEnabled()) {
 362  73
             log.info(messages.getMessage("resolve.inheritance.begin"));
 363  
         }
 364  
 
 365  
         // fixup heritage links
 366  73
         if (log.isInfoEnabled()) {
 367  73
             log.info(messages.getMessage("finding.parents"));
 368  
         }
 369  
 
 370  73
         Iterator di = displayElements.entrySet().iterator();
 371  7515
         while (di.hasNext()) {
 372  7442
             Map.Entry e = (Map.Entry) di.next();
 373  7442
             ComponentBean b = (ComponentBean) e.getValue();
 374  
 
 375  
             try {
 376  7442
                 assignParent(b);
 377  
             } catch (RuntimeException e1) {
 378  
                 log.error(messages.getMessage("finding.parents.exception"), e1);
 379  
                 throw e1;
 380  7442
             }
 381  
 
 382  7442
             b = null;
 383  7442
             e = null;
 384  7442
         }
 385  73
         di = null;
 386  
 
 387  73
         if (log.isInfoEnabled()) {
 388  73
             log.info(messages.getMessage("checking.inheritance"));
 389  
         }
 390  
 
 391  
         // Check for circular inheritance
 392  73
         di = displayElements.entrySet().iterator();
 393  7515
         while (di.hasNext()) {
 394  7442
             Map.Entry e = (Map.Entry) di.next();
 395  7442
             ComponentBean b = (ComponentBean) e.getValue();
 396  
 
 397  
             try {
 398  7442
                 checkCircularInheritance(b);
 399  
             } catch (RuntimeException e1) {
 400  
                 log.error(
 401  
                         messages.getMessage("checking.inheritance.exception"),
 402  
                         e1);
 403  
                 throw e1;
 404  7442
             }
 405  
 
 406  7442
             b = null;
 407  7442
             e = null;
 408  7442
         }
 409  73
         di = null;
 410  
 
 411  73
         if (log.isInfoEnabled()) {
 412  73
             log.info(messages.getMessage("realizing.inheritance"));
 413  
         }
 414  
 
 415  
         // now realizing inheritance
 416  73
         di = displayElements.entrySet().iterator();
 417  7515
         while (di.hasNext()) {
 418  7442
             Map.Entry e = (Map.Entry) di.next();
 419  7442
             ComponentBean b = (ComponentBean) e.getValue();
 420  
 
 421  
             try {
 422  7442
                 realizingInheritance(b);
 423  
             } catch (RuntimeException e1) {
 424  
                 log.error(messages.getMessage("realizing.inheritance.exception"), e1);
 425  
                 throw e1;
 426  7442
             }
 427  
 
 428  
             //check to make sure that there is not a duplicate component id
 429  
             //within the same naming container.
 430  7442
             checkTree(b);
 431  
 
 432  7442
             b = null;
 433  7442
             e = null;
 434  7442
         }
 435  73
         di = null;
 436  
 
 437  73
         if (log.isInfoEnabled()) {
 438  73
             log.info(messages.getMessage("resolve.inheritance.end"));
 439  
         }
 440  
 
 441  73
     }
 442  
 
 443  
     /**
 444  
      * <p>Returns the root metadata component that is used to add to the component
 445  
      * tree.  This method might be overridden to broaden the scope to search for
 446  
      * components outside of the <code>displayElement</code> cache.</p>
 447  
      *
 448  
      * @param jsfid id of a component bean
 449  
      * @return component bean
 450  
      */
 451  
     protected ComponentBean getTopLevelElement(String jsfid) {
 452  
 
 453  
         // find the top-level display element associated with the subtree
 454  7636
         ComponentBean b = (ComponentBean) displayElements.get(jsfid);
 455  7636
         if (b == null) {
 456  
             throw new NullPointerException(messages.getMessage(
 457  
                     "jsfid.notfound", new Object[] { jsfid }));
 458  
         }
 459  
 
 460  7636
         return b;
 461  
     }
 462  
 
 463  
 
 464  
     /**
 465  
      * <p>Called to assign the IsA parent to the {@link ComponentBean}
 466  
      * using the <code>extends</code> attribute.
 467  
      * </p>
 468  
      *
 469  
      * @param b component bean needing isa parent assigned
 470  
      */
 471  
     public void assignParent(ComponentBean b) {
 472  
 
 473  8693
         synchronized (displayElements) {
 474  
 
 475  8693
             if (b instanceof InnerComponentBean) {
 476  
 
 477  
                 // these elements are like inner classes
 478  
                 // set the extends to the element so the
 479  
                 // parent can be generically resolved
 480  1251
                 if (b.getJsfid() != null) {
 481  1251
                     b.setExtends(b.getJsfid());
 482  
                 }
 483  
             }
 484  
 
 485  
             // look for a meta inheritance property
 486  8693
             if (b.getExtends() != null) {
 487  
 
 488  
                 // assign the parent to a top-level display element
 489  8279
                 b.setIsAParent(getTopLevelElement(b.getExtends()));
 490  
             }
 491  
 
 492  
             // resolve inheritance of nested components
 493  8693
             Iterator ci = b.getChildrenIterator();
 494  9571
             while (ci.hasNext()) {
 495  878
                 assignParent((ComponentBean) ci.next());
 496  878
             }
 497  
 
 498  
             // resolve inheritance of converter
 499  8693
             if (b.getConverter() != null) {
 500  25
                 assignParent(b.getConverter());
 501  
             }
 502  
 
 503  
             // resolve inheritance of validators
 504  8693
             Iterator vi = b.getValidatorIterator();
 505  8710
             while (vi.hasNext()) {
 506  17
                 assignParent((ComponentBean) vi.next());
 507  17
             }
 508  
 
 509  
             // resolve inheritance of value change listeners
 510  8693
             vi = b.getValueChangeListenerIterator();
 511  8710
             while (vi.hasNext()) {
 512  17
                 assignParent((ComponentBean) vi.next());
 513  17
             }
 514  8693
             vi = null;
 515  
 
 516  
             // resolve inheritance of action listeners
 517  8693
             vi = b.getActionListenerIterator();
 518  8710
             while (vi.hasNext()) {
 519  17
                 assignParent((ComponentBean) vi.next());
 520  17
             }
 521  8693
             vi = null;
 522  
 
 523  8693
         }
 524  
 
 525  8693
     }
 526  
 
 527  
     /**
 528  
      * <p>This overload handles fixing up {@link AttributeBean}
 529  
      * inheritance.</p>
 530  
      *
 531  
      * @param a attribute needing inheritance resolved
 532  
      */
 533  
     protected void realizingInheritance(AttributeBean a) {
 534  
 
 535  
         // look to see if the inheritance has
 536  
         // already been resolved
 537  3276
         if (a.isInheritanceFinal()) {
 538  20
             return;
 539  
         }
 540  
 
 541  
         // if no parent, nothing to do
 542  3256
         if (a.getIsAParent() == null) {
 543  1618
             return;
 544  
         }
 545  
 
 546  
         // traverse up to the parent and work down
 547  1638
         realizingInheritance(a.getIsAParent());
 548  
 
 549  
         // inherit attribute value
 550  1638
         if (a.getValue() == null) {
 551  26
             a.setValue(a.getIsAParent().getValue());
 552  
         }
 553  
 
 554  
         // inherit late binding type
 555  1638
         if (a.getBindingType() == null) {
 556  1222
             a.setBindingType(a.getIsAParent().getBindingType());
 557  
         }
 558  
 
 559  1638
         if (a.getDescription() == null) {
 560  1623
             a.setDescription(a.getIsAParent().getDescription());
 561  
         }
 562  
 
 563  
         // set final indicator
 564  1638
         a.setInheritanceFinal(true);
 565  
 
 566  1638
     }
 567  
 
 568  
     /**
 569  
      * <p>This method is passed a {@link ComponentBean} and is
 570  
      * recursively called for each contained component.  It fixes up
 571  
      * the meta inheritance relationships.
 572  
      * </p>
 573  
      *
 574  
      * @param b component bean needing inheritance realized
 575  
      */
 576  
     public void realizingInheritance(ComponentBean b) {
 577  
 
 578  
         // look at the final indicator to determine
 579  
         // if inheritance has already been resolved
 580  16120
         if (b.isInheritanceFinal()) {
 581  8000
             return;
 582  
         }
 583  
 
 584  
         // does the node have a parent
 585  8120
         if (b.getIsAParent() != null) {
 586  
 
 587  
             // resolve the parents inheritance and
 588  
             // work down
 589  7707
             realizingInheritance(b.getIsAParent());
 590  
 
 591  
             // inherit component type
 592  7707
             if (b.getComponentType() == null) {
 593  5709
                 b.setComponentType(b.getIsAParent().getComponentType());
 594  
             }
 595  
 
 596  7707
             if (b.getAllowBody() == null) {
 597  7417
                 b.setAllowBody(b.getIsAParent().getAllowBody());
 598  
             }
 599  
 
 600  7707
             if (b.getFacetName() == null) {
 601  7707
                 b.setFacetName(b.getIsAParent().getFacetName());
 602  
             }
 603  
 
 604  7707
             if (b.getDescription() == null) {
 605  7539
                 b.setDescription(b.getIsAParent().getDescription());
 606  
             }
 607  
 
 608  
             // inherit parents attributes
 609  7707
             Iterator pi = b.getIsAParent().getAttributeIterator();
 610  105177
             while (pi.hasNext()) {
 611  97470
                 AttributeBean a = (AttributeBean) pi.next();
 612  97470
                 if (a != null) {
 613  
                     // if the parent has and attribute the child doesn't
 614  
                     // then the child inherits it
 615  97470
                     if ((a.getName() != null)
 616  
                         && (!b.getAttributes().containsKey(a.getName()))) {
 617  95832
                         b.getAttributes().put(a.getName(), a);
 618  95832
                     } else if (
 619  
                             (a.getName() != null)
 620  
                             && (b.getAttributes().containsKey(a.getName()))) {
 621  
                         // if the parent has an attribute, let the child
 622  
                         // attribute inherit properties of the attribute
 623  1638
                         AttributeBean ca =
 624  
                                 (AttributeBean) b.getAttributes().get(a.getName());
 625  1638
                         ca.setIsAParent(a);
 626  1638
                         realizingInheritance(ca);
 627  
                     }
 628  
 
 629  
                 }
 630  97470
                 a = null;
 631  97470
             }
 632  7707
             pi = null;
 633  
 
 634  
             //inherit symbols
 635  7707
             pi = b.getIsAParent().getSymbols().entrySet().iterator();
 636  13039
             while (pi.hasNext()) {
 637  5332
                Map.Entry e = (Map.Entry) pi.next();
 638  5332
                if (!b.getSymbols().containsKey(e.getKey())) {
 639  5302
                   b.getSymbols().put(e.getKey(), e.getValue());
 640  
                }
 641  5332
             }
 642  
 
 643  
             //inherit elements from the parent.  elements are identified/ordered by
 644  
             //the renderid in the set.
 645  7707
             TreeSet tmp = new TreeSet();
 646  
             // get the parents children/elements
 647  7707
             Iterator ci = b.getIsAParent().getChildrenIterator();
 648  7901
             while (ci.hasNext()) {
 649  194
                 ComponentBean c = (ComponentBean) ci.next();
 650  
                 // the parent of a element can only be a display element
 651  
                 // and a display element must have or inherit a component type
 652  194
                 if (c.getComponentType() == null) {
 653  
                     throw new NullPointerException(messages.getMessage("missing.componentType.exception",
 654  
                             new Object[] {c}));
 655  
                 }
 656  
 
 657  
                 // if the child node doesn't contain the parent's
 658  
                 // element identified by renderId then add it to a
 659  
                 // temp set
 660  194
                 if (!b.getChildren().contains(c)) {
 661  192
                     tmp.add(c);
 662  
                 }
 663  
 
 664  194
                 c = null;
 665  194
             }
 666  
 
 667  
             // merge the delta of parent elements not found in the
 668  
             // child's element set into the child's element set
 669  7707
             b.setChildren(tmp);
 670  7707
             tmp = null;
 671  7707
             ci = null;
 672  
 
 673  
         }
 674  
 
 675  
         // at this point, the component id should be defined or inherited
 676  8120
         if (b.getComponentType() == null) {
 677  
             throw new NullPointerException(messages.getMessage("missing.componentType.exception",
 678  
                     new Object[] {b}));
 679  
         }
 680  
 
 681  
         // iterate thru the inherited set of elements resolving
 682  
         // their inheritance
 683  8120
         Iterator ci = b.getChildren().iterator();
 684  8694
         while (ci.hasNext()) {
 685  574
             ComponentBean c = (ComponentBean) ci.next();
 686  574
             realizingInheritance(c);
 687  574
             if (c.getComponentType() == null) {
 688  
                 throw new NullPointerException(messages.getMessage("missing.componentType.exception",
 689  
                         new Object[] {c}));
 690  
             }
 691  574
             c = null;
 692  574
         }
 693  8120
         ci = null;
 694  
 
 695  
         // inherit converter from parent
 696  8120
         if (b.getConverter() == null && b.getIsAParent() != null
 697  
                 && b.getIsAParent().getConverter() != null) {
 698  17
             b.addConverter((ConverterBean) b.getIsAParent().getConverter());
 699  
         }
 700  
 
 701  
         // resolve the inheritance of a nested converter
 702  8120
         if (b.getConverter() != null) {
 703  31
             realizingInheritance(b.getConverter());
 704  
         }
 705  
 
 706  
         // inheritance of all parent validators
 707  8120
         if (b.getIsAParent() != null) {
 708  7707
            Iterator vi = b.getIsAParent().getValidatorIterator();
 709  7720
            while (vi.hasNext()) {
 710  13
                ComponentBean c = (ComponentBean) vi.next();
 711  
                // check to make sure the child doesn't have one
 712  13
                if (!b.getValidators().contains(c)) {
 713  13
                   b.addValidator((ValidatorBean) c);
 714  
                }
 715  13
                c = null;
 716  13
            }
 717  7707
            vi = null;
 718  
         }
 719  
 
 720  
         // resovle inheritance of all nested validators
 721  8120
         Iterator vi = b.getValidatorIterator();
 722  8143
         while (vi.hasNext()) {
 723  23
             ComponentBean c = (ComponentBean) vi.next();
 724  23
             realizingInheritance(c);
 725  23
             c = null;
 726  23
         }
 727  8120
         vi = null;
 728  
 
 729  
         // inheritance of all value change listeners
 730  8120
         if (b.getIsAParent() != null) {
 731  7707
            vi = b.getIsAParent().getValueChangeListenerIterator();
 732  7720
            while (vi.hasNext()) {
 733  13
               ComponentBean c = (ComponentBean) vi.next();
 734  13
               if (!b.getValueChangeListeners().contains(c)) {
 735  13
                  b.addValueChangeListener((ValueChangeListenerBean) c);
 736  
               }
 737  13
               c = null;
 738  13
            }
 739  7707
            vi = null;
 740  
         }
 741  
 
 742  
         // resolve inheritance of all nested value change listeners
 743  8120
         vi = b.getValueChangeListenerIterator();
 744  8143
         while (vi.hasNext()) {
 745  23
             ComponentBean c = (ComponentBean) vi.next();
 746  23
             realizingInheritance(c);
 747  23
             c = null;
 748  23
         }
 749  8120
         vi = null;
 750  
 
 751  
         // inheritance of all action listeners
 752  8120
         if (b.getIsAParent() != null) {
 753  7707
            vi = b.getIsAParent().getActionListenerIterator();
 754  7720
            while (vi.hasNext()) {
 755  13
               ComponentBean c = (ComponentBean) vi.next();
 756  13
               if (!b.getActionListeners().contains(c)) {
 757  13
                  b.addActionListener((ActionListenerBean) c);
 758  
               }
 759  13
               c = null;
 760  13
            }
 761  7707
            vi = null;
 762  
         }
 763  
 
 764  
         // resolve inheritance of all nested action listeners
 765  8120
         vi = b.getActionListenerIterator();
 766  8143
         while (vi.hasNext()) {
 767  23
             ComponentBean c = (ComponentBean) vi.next();
 768  23
             realizingInheritance(c);
 769  23
             c = null;
 770  23
             ci = null;
 771  23
         }
 772  8120
         vi = null;
 773  
 
 774  
         // toggle on the final flag
 775  8120
         b.setInheritanceFinal(true);
 776  8120
     }
 777  
 
 778  
     /**
 779  
      * <p>Walks up the isA parent chain looking for circular
 780  
      * relationships.  It returns a Stack of {@link ComponentBean}
 781  
      * documenting the heritage.  A runtime exception is thrown if
 782  
      * a circular relationship is found.
 783  
      * </p>
 784  
      *
 785  
      * @param b component bean having inheritance checked
 786  
      * @return inheritance stack
 787  
      */
 788  
     protected Stack getGeneralizations(ComponentBean b) {
 789  8396
         Stack heritage = new Stack();
 790  8396
         if (!(b instanceof InnerComponentBean)) {
 791  7442
             heritage.push(b);
 792  
         }
 793  
 
 794  8396
         ComponentBean node = b.getIsAParent();
 795  28794
         while (node != null) {
 796  20398
             if (!heritage.contains(node)) {
 797  20398
                 heritage.push(node);
 798  20398
             } else {
 799  
                 // construct a error message from the heritage stack
 800  
                 heritage.push(node);
 801  
                 throw new RuntimeException(messages.getMessage("circular.inheritance.exception",
 802  
                         new Object[] {describeRelationships(heritage)}));
 803  
             }
 804  20398
             node = node.getIsAParent();
 805  20398
         }
 806  
 
 807  8396
         return heritage;
 808  
     }
 809  
 
 810  
     /**
 811  
      * <p>Walks up the hasA parent chain looking for circular
 812  
      * relationships.  It returns a Stack of {@link ComponentBean}
 813  
      * documenting the composition.  A runtime exception is thrown if
 814  
      * a circular relationship is found.
 815  
      * </p>
 816  
      *
 817  
      * @param b component bean having composition checked
 818  
      * @return stack of parents
 819  
      */
 820  
     protected Stack getAssociations(ComponentBean b) {
 821  8396
         Stack relationships = new Stack();
 822  8396
         ComponentBean node = b.getHasAParent();
 823  11796
         while (node != null) {
 824  3400
             if (!relationships.contains(node)) {
 825  3400
                 relationships.push(node);
 826  3400
             } else {
 827  
                 relationships.push(node);
 828  
 
 829  
                 throw new RuntimeException(messages.getMessage("circular.composition.exception",
 830  
                         new Object[] {describeRelationships(relationships)}));
 831  
             }
 832  3400
             node = node.getIsAParent();
 833  3400
         }
 834  8396
         return relationships;
 835  
     }
 836  
 
 837  
     /**
 838  
      * <p>Returns a StringBuffer with an xpath like expression of
 839  
      * <code>jsfid</code> that describes the Stack of {@link ComponentBean}.
 840  
      * </p>
 841  
      *
 842  
      * @param heritage stack of relationships to report on
 843  
      * @return description of the stack
 844  
      */
 845  
     protected StringBuffer describeRelationships(Stack heritage) {
 846  
         StringBuffer msg = new StringBuffer();
 847  
         for (int i = 0; i < heritage.size(); i++) {
 848  
             ComponentBean node = (ComponentBean) heritage.get(i);
 849  
             if (i > 0) {
 850  
                 msg.insert(0, "/");
 851  
             }
 852  
             msg.insert(0, node.getJsfid());
 853  
         }
 854  
         return msg;
 855  
     }
 856  
 
 857  
     /**
 858  
      * <p>Passed a {@link ComponentBean}, the method looks for several
 859  
      * types of circular inheritances.  It's recursively called for all
 860  
      * contained components, children, validators, actionListeners,
 861  
      * valueChangeListeners and Converter. A runtime exception is
 862  
      * thrown if a invalid relationship is found.
 863  
      * </p>
 864  
      *
 865  
      * @param b component bean to check
 866  
      */
 867  
     protected void checkCircularInheritance(ComponentBean b) {
 868  
 
 869  8396
         Stack associations = getAssociations(b);
 870  8396
         Stack generalizations = getGeneralizations(b);
 871  
 
 872  8396
         if ((b.getHasAParent() != null)
 873  
           && (b.getIsAParent() != null)
 874  
           && (b.getHasAParent() == b.getIsAParent())) {
 875  
 
 876  
             throw new RuntimeException(messages.getMessage("circular.child.parent.same.exception",
 877  
                     new Object[] {describeRelationships(generalizations), describeRelationships(associations) }));
 878  
         }
 879  
 
 880  8396
         if ((b.getHasAParent() != null)
 881  
         && generalizations.contains(b.getHasAParent())) {
 882  
 
 883  
             throw new RuntimeException(messages.getMessage("circular.child.extends.same.parent.exception",
 884  
                     new Object[] {describeRelationships(generalizations),
 885  
                             describeRelationships(getGeneralizations(b.getHasAParent()))}));
 886  
         }
 887  
 
 888  8396
         associations.clear();
 889  8396
         generalizations.clear();
 890  
 
 891  8396
         associations = null;
 892  8396
         generalizations = null;
 893  
 
 894  8396
         Iterator ci = b.getChildrenIterator();
 895  9274
         while (ci.hasNext()) {
 896  878
             checkCircularInheritance((ComponentBean) ci.next());
 897  878
         }
 898  8396
         ci = null;
 899  
 
 900  8396
         if (b.getConverter() != null) {
 901  25
             checkCircularInheritance(b.getConverter());
 902  
         }
 903  
 
 904  8396
         Iterator vi = b.getValidatorIterator();
 905  8413
         while (vi.hasNext()) {
 906  17
             checkCircularInheritance((ComponentBean) vi.next());
 907  17
         }
 908  
 
 909  8396
         vi = b.getValueChangeListenerIterator();
 910  8413
         while (vi.hasNext()) {
 911  17
             checkCircularInheritance((ComponentBean) vi.next());
 912  17
         }
 913  
 
 914  8396
         vi = b.getActionListenerIterator();
 915  8413
         while (vi.hasNext()) {
 916  17
             checkCircularInheritance((ComponentBean) vi.next());
 917  17
         }
 918  
 
 919  8396
         vi = null;
 920  
 
 921  8396
     }
 922  
 
 923  
     /**
 924  
      * <p>Recursively called to unassign isA and hasA parent
 925  
      * relationships.
 926  
      * </p>
 927  
      *
 928  
      * @param b component bean
 929  
      */
 930  
     protected void unassignParent(ComponentBean b) {
 931  
 
 932  
         // play nicely and clean up your mess
 933  24492
         if (b.getIsAParent() != null) {
 934  17020
             unassignParent(b.getIsAParent());
 935  
         }
 936  7472
         b.setHasAParent(null);
 937  
 
 938  7472
         Iterator ai = b.getAttributeIterator();
 939  18430
         while (ai.hasNext()) {
 940  10958
             AttributeBean a = (AttributeBean) ai.next();
 941  10958
             a.setHasAParent(null);
 942  10958
             a.setIsAParent(null);
 943  10958
             a = null;
 944  10958
         }
 945  7472
         ai = null;
 946  
 
 947  7472
         Iterator ci = b.getChildrenIterator();
 948  7472
         while (ci.hasNext()) {
 949  31
             unassignParent((ComponentBean) ci.next());
 950  
         }
 951  7441
         b.getChildren().clear();
 952  
 
 953  7441
         if (b.getConverter() != null) {
 954  
             unassignParent(b.getConverter());
 955  
         }
 956  7441
         b.addConverter(null);
 957  
 
 958  
         Iterator vi = b.getValidatorIterator();
 959  
         while (vi.hasNext()) {
 960  
             unassignParent((ComponentBean) vi.next());
 961  
         }
 962  
         b.getValidators().clear();
 963  
 
 964  
         vi = b.getValueChangeListenerIterator();
 965  
         while (vi.hasNext()) {
 966  
             unassignParent((ComponentBean) vi.next());
 967  
         }
 968  
         b.getValueChangeListeners().clear();
 969  
 
 970  
         vi = b.getActionListenerIterator();
 971  
         while (vi.hasNext()) {
 972  
             unassignParent((ComponentBean) vi.next());
 973  
         }
 974  
         b.getValueChangeListeners().clear();
 975  
 
 976  
         vi = null;
 977  
 
 978  
     }
 979  
 
 980  
 
 981  
     /**
 982  
      * <p>Cleans up before a group of files are reloaded.</p>
 983  
      *
 984  
      * @param watchDogName group name for a group of config files or templates
 985  
      */
 986  
     protected void clear(String watchDogName) {
 987  185
         Iterator di = displayElements.entrySet().iterator();
 988  7626
         while (di.hasNext()) {
 989  7441
             Map.Entry e = (Map.Entry) di.next();
 990  7441
             ComponentBean b = (ComponentBean) e.getValue();
 991  
 
 992  
             try {
 993  7441
                 unassignParent(b);
 994  7441
             } catch (RuntimeException e1) {
 995  
                 // log.error(e1);
 996  
             }
 997  
 
 998  7441
             b = null;
 999  7441
             e = null;
 1000  7441
         }
 1001  185
         di = null;
 1002  
 
 1003  185
         displayElements.clear();
 1004  185
     }
 1005  
 
 1006  
     /**
 1007  
      * <p>The destroy method is invoked to clean up resources.  By
 1008  
      * dereferencing the complex graph of display elements
 1009  
      * </p>
 1010  
      */
 1011  
     public void destroy() {
 1012  144
         clear(Globals.DEFAULT_COMPONENT_CONFIG_WATCHDOG);
 1013  144
         context = null;
 1014  
 
 1015  144
         if (parser != null) {
 1016  120
             parser.setConfig(null);
 1017  
         }
 1018  144
         parser = null;
 1019  
 
 1020  144
         if (watchDogs != null) {
 1021  120
             Iterator wi = watchDogs.entrySet().iterator();
 1022  194
             while (wi.hasNext()) {
 1023  74
                 Map.Entry e = (Map.Entry) wi.next();
 1024  74
                 WatchDog watchDog = (WatchDog) e.getValue();
 1025  74
                 if (watchDog != null) {
 1026  74
                     watchDog.destroy();
 1027  
                 }
 1028  74
             }
 1029  120
             watchDogs.clear();
 1030  120
             watchDogs = null;
 1031  
         }
 1032  
 
 1033  144
     }
 1034  
 
 1035  
     /**
 1036  
      * <p>Called by the {@link ConfigBeanFactory} to determine if this
 1037  
      * instance of {@link ConfigBean} can handle finding the {@link ConfigBean}
 1038  
      * from the <code>jsfid</code>.
 1039  
      *
 1040  
      * @param id jsfid
 1041  
      * @return <code>true</code> if the jsfid can be handled
 1042  
      */
 1043  
     public boolean validMoniker(String id) {
 1044  
 
 1045  2840
         for (int i = 0; i < suffixes.length; i++) {
 1046  1901
            if (id.endsWith(suffixes[i])) {
 1047  23
                return false;
 1048  
            }
 1049  
         }
 1050  
 
 1051  939
         return true;
 1052  
     }
 1053  
 
 1054  
     /**
 1055  
      * <p>Implementation of the Comparable interface. The <code>weight</code>
 1056  
      * is used to determine the ordering of the registered {@link ConfigBean}
 1057  
      * objects within the {@link ConfigBeanFactory}.
 1058  
      * </p>
 1059  
      *
 1060  
      * @param config object to compare to
 1061  
      * @return compares the weight of two config handlers
 1062  
      */
 1063  
     public int compareTo(Object config) {
 1064  
 
 1065  144
         ConfigBean compConfig = (ConfigBean) config;
 1066  144
         if (getWeight() > compConfig.getWeight()) {
 1067  144
             return 1;
 1068  
         } else if (getWeight() > compConfig.getWeight()) {
 1069  
             return -1;
 1070  
         } else {
 1071  
             return 0;
 1072  
         }
 1073  
 
 1074  
     }
 1075  
     /**
 1076  
      * <p>The weight is an attempt to make a plug-able system for
 1077  
      * registering {@link ConfigBean} objects with the {@link ConfigBeanFactory}.
 1078  
      * A custom implementation could be registered for a different composition
 1079  
      * technique adding or overriding an existing implementation.
 1080  
      * </p>
 1081  
      *
 1082  
      * @return <code>0</code>
 1083  
      */
 1084  
     public int getWeight() {
 1085  96
         return 0;
 1086  
     }
 1087  
 
 1088  
     /**
 1089  
      * <p>This class defines a single configration file that is watched for
 1090  
      * changes.  In addition to the <code>URL</code> passed to the overloaded
 1091  
      * constructor, the <code>lastModifed</code> date is kept as a state
 1092  
      * variable.</p>
 1093  
      */
 1094  
     protected class XmlConfigDef implements ConfigBean.ConfigDefinition {
 1095  
         /**
 1096  
          * <p>The location of the config file.</p>
 1097  
          */
 1098  87
         private URL configUrl = null;
 1099  
 
 1100  
         /**
 1101  
          * <p>Date the last time the file was modified as a <code>long</code>.</p>
 1102  
          */
 1103  87
         private long lastModified = 0;
 1104  
 
 1105  
         /**
 1106  
          * <p>Overloaded constructor that requires the target config <code>URL</code>.
 1107  
          *
 1108  
          * @param configUrl file to load
 1109  
          */
 1110  87
         public XmlConfigDef(URL configUrl) {
 1111  87
            this.configUrl = configUrl;
 1112  87
         }
 1113  
 
 1114  
         /**
 1115  
          * <p>Returns the target configuration file url.</p>
 1116  
          *
 1117  
          * @return file to load
 1118  
          */
 1119  
         public URL getConfigUrl() {
 1120  176
            return configUrl;
 1121  
         }
 1122  
 
 1123  
         /**
 1124  
          * <p>Returns the last time the target configuration file was modified.</p>
 1125  
          *
 1126  
          * @return last modified timestamp of the config file
 1127  
          */
 1128  
         public long getLastModified() {
 1129  35
            return lastModified;
 1130  
         }
 1131  
 
 1132  
         /**
 1133  
          * <p>Sets the last time the target configuration file was modified.</p>
 1134  
          *
 1135  
          * @param lastModified last time the file was changed
 1136  
          */
 1137  
         public void setLastModified(long lastModified) {
 1138  87
            this.lastModified = lastModified;
 1139  87
         }
 1140  
      }
 1141  
 
 1142  
     /**
 1143  
      * <p>This inner class watches for changes in a array of {@link ConfigBean.ConfigDefinition}'s.
 1144  
      * This collection defines the configuration files that the {@link org.apache.shale.clay.component.Clay}
 1145  
      * component uses.</p>
 1146  
      */
 1147  
     protected class WatchDog {
 1148  
 
 1149  
         /**
 1150  
          * <p>Name assigned to the resource watcher.</p>
 1151  
          */
 1152  74
         private String name = null;
 1153  
 
 1154  
         /**
 1155  
          * <p>Returns the name of the resource watcher.</p>
 1156  
          *
 1157  
          * @return the watched resource
 1158  
          */
 1159  
         public String getName() {
 1160  235
            return name;
 1161  
         }
 1162  
 
 1163  
         /**
 1164  
          * <p>Array of config file definitions.</p>
 1165  
          */
 1166  74
         private ConfigBean.ConfigDefinition[] configDefs = null;
 1167  
 
 1168  
         /**
 1169  
          * <p>Array of connections used to determine that the file has changed.</p>
 1170  
          */
 1171  74
         private URLConnection[] connections = null;
 1172  
 
 1173  
         /**
 1174  
          * <p>Overloaded constructor that is passed the configuration file
 1175  
          * definitions as a parameter.  The name associated with the resource
 1176  
          * watcher is also passed as a parameter.</p>
 1177  
          *
 1178  
          * @param configDefs files in the watch group
 1179  
          * @param name the watch group name
 1180  
          */
 1181  74
         public WatchDog(ConfigBean.ConfigDefinition[] configDefs, String name) {
 1182  74
             this.configDefs = configDefs;
 1183  74
             this.name = name;
 1184  74
         }
 1185  
 
 1186  
         /**
 1187  
          * <p>This method is invoked to dereference the private
 1188  
          * array of config file definitions.</p>
 1189  
          */
 1190  
         public void destroy() {
 1191  74
             close();
 1192  161
             for (int i = 0; i < configDefs.length; i++) {
 1193  87
                 configDefs[i] = null;
 1194  
             }
 1195  
 
 1196  74
             configDefs = null;
 1197  74
         }
 1198  
 
 1199  
         /**
 1200  
          * <p>Loads an array of <code>URLConnection</code> corresponding to the
 1201  
          * <code>configDefs</code>'s.</p>
 1202  
          */
 1203  
         private void open() {
 1204  
 
 1205  76
             if (connections != null) {
 1206  1
                 close();
 1207  
             }
 1208  
 
 1209  76
             connections = new URLConnection[configDefs.length];
 1210  165
             for (int i = 0; i < configDefs.length; i++) {
 1211  
 
 1212  
                 try {
 1213  89
                     connections[i] = configDefs[i].getConfigUrl()
 1214  
                             .openConnection();
 1215  
                 } catch (IOException e) {
 1216  
                     log.error(messages.getMessage("parser.load.error",
 1217  
                             new Object[] { configDefs[i].getConfigUrl()
 1218  
                                     .getPath() }), e);
 1219  89
                 }
 1220  
             }
 1221  76
         }
 1222  
 
 1223  
         /**
 1224  
          * <p>Performs some extra cleanup on the open array of
 1225  
          * <code>connections</code>.</p>
 1226  
          */
 1227  
         private void close() {
 1228  151
             if (connections == null) {
 1229  75
                 return;
 1230  
             }
 1231  
 
 1232  165
             for (int i = 0; i < connections.length; i++) {
 1233  89
                 connections[i] = null;
 1234  
             }
 1235  
 
 1236  76
             connections = null;
 1237  76
         }
 1238  
 
 1239  
         /**
 1240  
          * <p>Iterates over the open <code>connections</code> looking
 1241  
          * for files that have changed.  A <code>true</code> value is
 1242  
          * returned if one of the <code>configDefs</code> has been
 1243  
          * modified since last loaded.</p>
 1244  
          *
 1245  
          * @return <code>true</code> if the file has been modified
 1246  
          */
 1247  
         private boolean isDirty() {
 1248  37
             for (int i = 0; i < configDefs.length; i++) {
 1249  35
                 if (configDefs[i].getLastModified() < connections[i]
 1250  
                         .getLastModified()) {
 1251  33
                     return true;
 1252  
                 }
 1253  
             }
 1254  2
             return false;
 1255  
         }
 1256  
 
 1257  
         /**
 1258  
          * <p>This method is the watch dog timmer.  It's invoked to determine
 1259  
          * if any of the files have changed since the last time they were loaded.
 1260  
          * If a change has occured on any of the <code>configDefs</code> or
 1261  
          * the <code>forceReload</code> param is <code>true</code>, all the
 1262  
          * files are reloaded and the last modified date is reset in the
 1263  
          * {@link ConfigBean.ConfigDefinition}. A <code>true</code> value is
 1264  
          * returned if the files were refreshed.
 1265  
          * </p>
 1266  
          *
 1267  
          * @param forceReload reload the group of config files
 1268  
          * @return <code>true</code> if the group was reloaded
 1269  
          */
 1270  
         public boolean refresh(boolean forceReload) {
 1271  
 
 1272  76
             boolean wasDirty = false;
 1273  
 
 1274  76
             int i = 0;
 1275  
             try {
 1276  76
                 open();
 1277  76
                 if (forceReload || isDirty()) {
 1278  74
                     wasDirty = true;
 1279  74
                     clear(getName());
 1280  160
                     for (i = 0; i < configDefs.length; i++) {
 1281  
 
 1282  87
                         if (log.isInfoEnabled()) {
 1283  87
                             log.info(messages.getMessage("parser.load.file",
 1284  
                                     new Object[] { configDefs[i].getConfigUrl()
 1285  
                                             .getPath() }));
 1286  
                         }
 1287  
 
 1288  
                         try {
 1289  
 
 1290  87
                             configDefs[i].setLastModified(connections[i]
 1291  
                                     .getLastModified());
 1292  87
                             parser.loadConfigFile(connections[i].getURL(), getName());
 1293  
 
 1294  
                         } catch (IOException e) {
 1295  
                             log.error(messages.getMessage("parser.load.error",
 1296  
                                     new Object[] { configDefs[i].getConfigUrl()
 1297  
                                             .getPath() }), e);
 1298  
                         } catch (SAXException e) {
 1299  
                             log.error(messages.getMessage("parser.load.error",
 1300  
                                     new Object[] { configDefs[i].getConfigUrl()
 1301  
                                             .getPath() }), e);
 1302  86
                         }
 1303  
                     }
 1304  
 
 1305  73
                     resolveInheritance();
 1306  
                 }
 1307  
             } finally {
 1308  76
                 close();
 1309  76
             }
 1310  
 
 1311  75
             return wasDirty;
 1312  
         }
 1313  
 
 1314  
     }
 1315  
 
 1316  
     /**
 1317  
      * <p>This method should be called from key points in the application to invoke
 1318  
      * automatic reloading of the configuration files if they have been modified since
 1319  
      * last reloaded.  If the <code>forceReload</code> flag is <code>true</code> the files are
 1320  
      * reloaded.  A <code>true</code> return value indicates the config files
 1321  
      * were reloaded.</p>
 1322  
      *
 1323  
      * @param forceReload reload the files
 1324  
      * @return files were reloaded
 1325  
      */
 1326  
     public boolean refresh(boolean forceReload) {
 1327  
 
 1328  
         boolean wasDirty = false;
 1329  
 
 1330  
         WatchDog watchDog = (WatchDog) watchDogs.get(Globals.DEFAULT_COMPONENT_CONFIG_WATCHDOG);
 1331  
 
 1332  
         // is auto watch turned off
 1333  
         if (!ComponentConfigBean.this.isWatchDogOn || watchDog == null) {
 1334  
            return wasDirty;
 1335  
         }
 1336  
 
 1337  
         synchronized (displayElements) {
 1338  
             wasDirty = watchDog.refresh(forceReload);
 1339  
         }
 1340  
 
 1341  
         watchDog = null;
 1342  
 
 1343  
         return wasDirty;
 1344  
     }
 1345  
 
 1346  
 
 1347  
     /**
 1348  
      * <p>A static string array of faces component types that are naming
 1349  
      * containers.</p>
 1350  
      */
 1351  1
     protected static final String[] NAMING_CONTAINER_TYPES = {
 1352  
         "javax.faces.HtmlForm",
 1353  
         "javax.faces.HtmlDataTable",
 1354  
         "org.apache.shale.view.Subview",
 1355  
         "javax.faces.NamingContainer"};
 1356  
 
 1357  
     /**
 1358  
      * <p>Checks the <code>componentType</code> against the <code>NAMING_CONTAINER_TYPES</code>
 1359  
      * list to determine if it is a naming container. Component id's must be unique within a
 1360  
      * naming container.  Returns a <code>true</code> value if the <code>componentType</code>
 1361  
      * is a naming container; otherwise, returns <code>false</code>.</p>
 1362  
      *
 1363  
      * @param componentType type of the component
 1364  
      * @return <code>true</code> if the component type is a naming comtainer
 1365  
      */
 1366  
     protected boolean isNamingContainer(String componentType) {
 1367  2190
        boolean flag = false;
 1368  10774
        for (int i = 0; i < NAMING_CONTAINER_TYPES.length; i++) {
 1369  8630
           if (NAMING_CONTAINER_TYPES[i].equals(componentType)) {
 1370  46
              flag = true;
 1371  46
              break;
 1372  
           }
 1373  
        }
 1374  
 
 1375  2190
        return flag;
 1376  
     }
 1377  
 
 1378  
 
 1379  
     /**
 1380  
      * <p>Recursively walks the tree of component metadata verifying
 1381  
      * there is not a duplicate component id within a naming container.
 1382  
      * A root {@link ComponentBean} is passed as a single parameter.
 1383  
      * The overloaded <code>checkTree(List, ComponentBean)</code> is
 1384  
      * invoked to process components under a naming container.</p>
 1385  
      *
 1386  
      * @param b root of the component tree
 1387  
      */
 1388  
      public void checkTree(ComponentBean b) {
 1389  7520
         if (log.isDebugEnabled()) {
 1390  
            log.debug(messages.getMessage("check.tree", new Object[] {b.getComponentType()}));
 1391  
         }
 1392  
 
 1393  7520
         List componentIds = new ArrayList();
 1394  7520
         checkTree(componentIds, b);
 1395  7519
         componentIds.clear();
 1396  7519
         componentIds = null;
 1397  7519
      }
 1398  
 
 1399  
 
 1400  
 
 1401  
      /**
 1402  
       * <p>Verifies there is not a duplicate component id within a naming container.
 1403  
       * A list of accumulating <code>componentIds</code> and a
 1404  
       * root {@link ComponentBean} is passed as parameters.  A runtime
 1405  
       * exception is thrown if a duplicate id is encountered.</p>
 1406  
       *
 1407  
       * @param componentIds list of component id's in the naming container
 1408  
       * @param b parent component bean
 1409  
       */
 1410  
       protected void checkTree(List componentIds, ComponentBean b) {
 1411  
 
 1412  
           //check fo duplicate component id's
 1413  9664
           String id = b.getId();
 1414  9664
           if (id != null && (id.indexOf('@') == -1)) {
 1415  981
               if (componentIds.contains(id)) {
 1416  1
                   throw new NullPointerException(messages.getMessage("duplicate.componentid.exception",
 1417  
                           new Object[] {id, b}));
 1418  
               } else {
 1419  980
                   componentIds.add(id);
 1420  
               }
 1421  
           }
 1422  
 
 1423  9663
           Iterator ci = b.getChildrenIterator();
 1424  11852
           while (ci.hasNext()) {
 1425  2190
              ComponentBean c = (ComponentBean) ci.next();
 1426  2190
              if (isNamingContainer(c.getComponentType())) {
 1427  46
                 checkTree(c);
 1428  46
              } else {
 1429  2144
                 checkTree(componentIds, c);
 1430  
              }
 1431  2189
           }
 1432  
 
 1433  9662
       }
 1434  
 
 1435  
 }