Coverage Report - org.webmacro.Broker
 
Classes in this File Line Coverage Branch Coverage Complexity
Broker
45%
118/262
40%
34/84
2.458
Broker$1
N/A
N/A
2.458
Broker$AutoLoaderSettingHandler
75%
6/8
N/A
2.458
Broker$MacroIncludeSettingHandler
12%
1/8
0%
0/2
2.458
Broker$ProviderSettingHandler
75%
6/8
N/A
2.458
 
 1  
 /*
 2  
  * Copyright (c) 1998, 1999 Semiotek Inc. All Rights Reserved.
 3  
  *
 4  
  * This software is the confidential intellectual property of
 5  
  * of Semiotek Inc.; it is copyrighted and licensed, not sold.
 6  
  * You may use it under the terms of the GNU General Public License,
 7  
  * version 2, as published by the Free Software Foundation. If you
 8  
  * do not want to use the GPL, you may still use the software after
 9  
  * purchasing a proprietary developers license from Semiotek Inc.
 10  
  *
 11  
  * This software is provided "as is", with NO WARRANTY, not even the
 12  
  * implied warranties of fitness to purpose, or merchantability. You
 13  
  * assume all risks and liabilities associated with its use.
 14  
  *
 15  
  * See the attached License.html file for details, or contact us
 16  
  * by e-mail at info@semiotek.com to get a copy.
 17  
  */
 18  
 
 19  
 
 20  
 package org.webmacro;
 21  
 
 22  
 import java.io.BufferedReader;
 23  
 import java.io.File;
 24  
 import java.io.FileInputStream;
 25  
 import java.io.FileNotFoundException;
 26  
 import java.io.IOException;
 27  
 import java.io.InputStream;
 28  
 import java.io.InputStreamReader;
 29  
 import java.io.OutputStream;
 30  
 import java.io.UnsupportedEncodingException;
 31  
 import java.lang.ref.WeakReference;
 32  
 import java.net.MalformedURLException;
 33  
 import java.net.URL;
 34  
 import java.util.Arrays;
 35  
 import java.util.Enumeration;
 36  
 import java.util.Hashtable;
 37  
 import java.util.Iterator;
 38  
 import java.util.Map;
 39  
 import java.util.Properties;
 40  
 import java.util.WeakHashMap;
 41  
 
 42  
 import org.slf4j.Logger;
 43  
 import org.slf4j.LoggerFactory;
 44  
 
 45  
 import org.webmacro.broker.ContextAutoLoader;
 46  
 import org.webmacro.engine.DefaultEvaluationExceptionHandler;
 47  
 import org.webmacro.engine.EvaluationExceptionHandler;
 48  
 import org.webmacro.engine.IntrospectionUtils;
 49  
 import org.webmacro.engine.MethodWrapper;
 50  
 import org.webmacro.engine.PropertyOperatorCache;
 51  
 import org.webmacro.util.Settings;
 52  
 import org.webmacro.util.SubSettings;
 53  
 
 54  
 import java.util.concurrent.ConcurrentHashMap;
 55  
 
 56  
 /**
 57  
  * The Broker is responsible for loading and initializing almost everything
 58  
  * in WebMacro. It reads a set of Properties and uses them to determine
 59  
  * which components of WebMacro should be loaded. It is also responsible
 60  
  * for loading in things like Templates, URLs, and so forth.
 61  
  * <p>
 62  
  * By default the Broker reads a file called WebMacro.properties, searching
 63  
  * your CLASSPATH and system CLASSPATH for it. There are constructors to
 64  
  * allow you to specify a different location, a URL, or even just supply
 65  
  * a properties object directly.
 66  
  * <p>
 67  
  * The most common WebMacro installation problems revolve around the
 68  
  * Broker. Without a properly configured Broker WebMacro is unable to
 69  
  * load templates, parse templates, fetch URLs, or perform most of its
 70  
  * other basic functions.
 71  
  *
 72  
  * @author Marcel Huijkman
 73  
  *
 74  
  * @version        23-07-2002
 75  
  */
 76  0
 public class Broker
 77  
 {
 78  
 
 79  
     public static final String WEBMACRO_DEFAULTS = "WebMacro.defaults";
 80  
     public static final String WEBMACRO_PROPERTIES = "WebMacro.properties";
 81  
     public static final String SETTINGS_PREFIX = "org.webmacro";
 82  
 
 83  2
     public static final WeakHashMap BROKERS = new WeakHashMap();
 84  
 
 85  
     private static Settings _defaultSettings;
 86  2
     protected static ClassLoader _myClassLoader = Broker.class.getClassLoader();
 87  2
     protected static ClassLoader _systemClassLoader = ClassLoader.getSystemClassLoader();
 88  
 
 89  6
     final protected Hashtable _providers = new Hashtable();
 90  6
     final protected Settings _config = new Settings();
 91  
     final protected String _name;
 92  6
     final public PropertyOperatorCache _propertyOperators
 93  
             = new PropertyOperatorCache();
 94  
 
 95  6
     protected Logger _log =  LoggerFactory.getLogger(Broker.class);
 96  
     
 97  
     private EvaluationExceptionHandler _eeHandler;
 98  
 
 99  
     /** a local map for one to dump stuff into, specific to this Broker */
 100  6
     private Map _brokerLocal = new ConcurrentHashMap();
 101  
 
 102  
     /** a local map for "global functions" */
 103  6
     private Map _functionMap = new ConcurrentHashMap();
 104  
 
 105  
     /** a local map for context tools and other automatic context goodies */
 106  6
     private Map _toolLoader = new ConcurrentHashMap();
 107  
 
 108  
     /** map of global macros */
 109  6
     private Map _macros = new ConcurrentHashMap();
 110  
 
 111  
     /*
 112  
 * Constructors.  Callers shouldn't use them; they should use the
 113  
 * factory methods (getBroker).
 114  
 *
 115  
 * Broker construction is kind of confusing.  There's a common
 116  
 * constructor, which initializes the log and a few other private
 117  
 * fields but doesn't do much else.  There's a bit of a
 118  
 * chicken-and-egg problem with loading the properties; we want to
 119  
 * be able to post log messages to indicate success or failure of
 120  
 * finding the configuration files, but we can't set a log target
 121  
 * until we do so.  The log system writes to standard error until
 122  
 * we set a target, so we let the constructor set up the default
 123  
 * target, then try and load properties, and then continue with the
 124  
 * setup by calling the various init() routines.
 125  
 */
 126  
 
 127  
     /**
 128  
      * Equivalent to Broker("WebMacro.properties"), except that it doesn't
 129  
      * complain if WebMacro.properties can't be found.
 130  
      */
 131  
     protected Broker () throws InitException
 132  
     {
 133  2
         this((Broker) null, WEBMACRO_PROPERTIES);
 134  2
         String propertySource = WEBMACRO_DEFAULTS + ", " + WEBMACRO_PROPERTIES
 135  
                 + ", " + "(System Properties)";
 136  2
         loadDefaultSettings();
 137  2
         loadSettings(WEBMACRO_PROPERTIES, true);
 138  2
         loadSystemSettings();
 139  2
         _log.info("Loaded settings from " + propertySource);
 140  2
         init();
 141  2
     }
 142  
 
 143  
     /**
 144  
      * Equivalent to Broker("WebMacro.properties"), except that it doesn't
 145  
      * complain if WebMacro.properties can't be found, and it loads properties
 146  
      * from a specified Properties.
 147  
      */
 148  
     protected Broker (Properties props) throws InitException
 149  
     {
 150  0
         this((Broker) null, "Ad-hoc Properties " + props.hashCode());
 151  0
         String propertySource = WEBMACRO_DEFAULTS + ", " + WEBMACRO_PROPERTIES
 152  
                 + ", (caller-supplied Properties), (System Properties)";
 153  0
         loadDefaultSettings();
 154  0
         loadSettings(WEBMACRO_PROPERTIES, true);
 155  0
         loadSettings(props);
 156  0
         loadSystemSettings();
 157  0
         _log.info("Loaded settings from " + propertySource);
 158  0
         init();
 159  0
     }
 160  
 
 161  
     /**
 162  
      * Search the classpath for the properties file under
 163  
      * the specified name.
 164  
      * @param fileName Use this name instead of "WebMacro.properties"
 165  
      */
 166  
     protected Broker (String fileName) throws InitException
 167  
     {
 168  0
         this((Broker) null, fileName);
 169  0
         String propertySource = WEBMACRO_DEFAULTS + ", " + fileName;
 170  0
         loadDefaultSettings();
 171  0
         boolean loaded = loadSettings(fileName, false);
 172  0
         if (!loaded)
 173  
         {
 174  0
             propertySource += "(not found)";
 175  
         }
 176  0
         loadSystemSettings();
 177  0
         propertySource += ", " + "(System Properties)";
 178  0
         _log.info("Loaded settings from " + propertySource);
 179  0
         init();
 180  0
     }
 181  
 
 182  
     /**
 183  
      * Explicitly provide the properties that WebMacro should
 184  
      * configure from. You also need to specify a name for this
 185  
      * set of properties so WebMacro can figure out whether
 186  
      * two brokers point at the same properties information.
 187  
      * @param dummy a Broker instance that is never used
 188  
      * @param name Two brokers are the "same" if they have the same name
 189  
      */
 190  
     protected Broker (Broker dummy, String name)
 191  
             throws InitException
 192  6
     {
 193  6
         _name = name;
 194  6
     }
 195  
 
 196  
     
 197  
     /**
 198  
      * Provide access to the broker's log.
 199  
      */
 200  
     public Logger getBrokerLog()
 201  
     {
 202  0
             return _log;
 203  
     }
 204  
 
 205  12
     private class ProviderSettingHandler extends Settings.ListSettingHandler
 206  
     {
 207  
         public void processSetting (String settingKey, String settingValue)
 208  
         {
 209  
             try
 210  
             {
 211  36
                 Class pClass = classForName(settingValue);
 212  36
                 Provider instance = (Provider) pClass.newInstance();
 213  36
                 addProvider(instance, settingKey);
 214  
             }
 215  0
             catch (Exception e)
 216  
             {
 217  0
                 _log.error("Provider (" + settingValue + ") failed to load", e);
 218  36
             }
 219  36
         }
 220  
     }
 221  
 
 222  12
     private class AutoLoaderSettingHandler extends Settings.ListSettingHandler
 223  
     {
 224  
         public void processSetting (String settingKey, String settingValue)
 225  
         {
 226  
             try
 227  
             {
 228  12
                 Class pClass = classForName(settingValue);
 229  12
                 ContextAutoLoader instance = (ContextAutoLoader) pClass.newInstance();
 230  12
                 instance.init(Broker.this, settingKey);
 231  
             }
 232  0
             catch (Exception e)
 233  
             {
 234  0
                 _log.error("ContextAutoLoader (" + settingValue + ") failed to load", e);
 235  12
             }
 236  12
         }
 237  
     }
 238  
 
 239  
 
 240  
     /**
 241  
      * Constructors should call this after they've set up the properties
 242  
      * to set up common things like profiling, providers, etc.
 243  
      */
 244  
     protected void init () throws InitException
 245  
     {
 246  
         String eehClass;
 247  
 
 248  
         // Initialize the property operator cache
 249  6
         _propertyOperators.init(this, _config);
 250  
 
 251  
         // Write out our properties as debug records
 252  6
         if (_log.isDebugEnabled())
 253  
         {
 254  0
             String[] properties = _config.getKeys();
 255  0
             Arrays.sort(properties);
 256  0
             for (int i = 0; i < properties.length; i++)
 257  
             {
 258  0
                 _log.debug("Property " + properties[i] + ": "
 259  
                         + _config.getSetting(properties[i]));
 260  
             }
 261  
         }
 262  
 
 263  
         // set up providers
 264  6
         _config.processListSetting("Providers", new ProviderSettingHandler());
 265  6
         if (_providers.size() == 0)
 266  
         {
 267  0
             _log.error("No Providers specified");
 268  0
             throw new InitException("No Providers specified in configuration");
 269  
         }
 270  
 
 271  6
         _config.processListSetting("ContextAutoLoaders", new AutoLoaderSettingHandler());
 272  
         // @@@ load autoloaders
 273  
 
 274  6
         eehClass = _config.getSetting("ExceptionHandler");
 275  6
         if (eehClass != null && !eehClass.equals(""))
 276  
         {
 277  
             try
 278  
             {
 279  6
                 _eeHandler = (EvaluationExceptionHandler)
 280  
                         classForName(eehClass).newInstance();
 281  
             }
 282  0
             catch (Exception e)
 283  
             {
 284  0
                 _log.warn("Unable to instantiate exception handler of class "
 285  
                         + eehClass + "; " + e);
 286  6
             }
 287  
         }
 288  6
         if (_eeHandler == null)
 289  
         {
 290  0
             _eeHandler = new DefaultEvaluationExceptionHandler();
 291  
         }
 292  
 
 293  6
         _eeHandler.init(this, _config);
 294  
 
 295  
         // Initialize function map
 296  6
         SubSettings fnSettings = new SubSettings(_config, "Functions");
 297  6
         String[] fns = fnSettings.getKeys();
 298  132
         for (int i = 0; fns != null && i < fns.length; i++)
 299  
         {
 300  126
             String fn = fns[i];
 301  126
             String fnSetting = fnSettings.getSetting(fn);
 302  126
             int lastDot = fnSetting.lastIndexOf('.');
 303  126
             if (lastDot == -1)
 304  
             {
 305  0
                 throw new IllegalArgumentException("Bad function declaration for "
 306  
                         + fn + ": " + fnSetting
 307  
                         + ".  This setting must include full class name followed by a '.' and method name");
 308  
             }
 309  126
             String fnClassName = fnSetting.substring(0, lastDot);
 310  126
             String fnMethName = fnSetting.substring(lastDot + 1);
 311  
             // function type may be static, instance, or factory.  Default is static
 312  126
             String fnType = _config.getSetting("Function." + fn + ".type", "static");
 313  126
             Object[] args = null;
 314  126
             if ("factory".equals(fnType))
 315  
             {
 316  
                 //TODO: implement this!!!
 317  
                 // get function from a factory method
 318  
                 // declared class/method is the factory class/instance method
 319  
                 // get the factory class method name
 320  
                 //String factoryMeth = _config.getSetting("Function." + fn + ".factory.method");
 321  
             }
 322  126
             if (!"static".equals(fnType))
 323  
             {
 324  
                 // get arg string
 325  0
                 String argString = _config.getSetting("Function." + fn + ".args");
 326  0
                 if (argString != null)
 327  
                 {
 328  0
                     if (!argString.startsWith("["))
 329  
                     {
 330  0
                         argString = "[" + argString + "]";
 331  
                     }
 332  0
                     org.webmacro.engine.StringTemplate tmpl =
 333  
                             new org.webmacro.engine.StringTemplate(this, "#set $args=" + argString);
 334  0
                     Context argContext = new Context(this);
 335  
                     try
 336  
                     {
 337  0
                         tmpl.evaluateAsString(argContext);
 338  
                     }
 339  0
                     catch (Exception e)
 340  
                     {
 341  0
                         _log.error("Unable to evaluate arguments to function " + fn
 342  
                                 + ".  The specified string was " + argString + ".", e);
 343  0
                     }
 344  0
                     args = (Object[]) argContext.get("args");
 345  0
                     _log.debug("Args for function " + fn + ": " + Arrays.asList(args));
 346  
                 }
 347  
             }
 348  
 
 349  126
             Class c = null;
 350  
             try
 351  
             {
 352  126
                 c = Class.forName(fnClassName);
 353  
             }
 354  0
             catch (Exception e)
 355  
             {
 356  0
                 _log.error("Unable to load class " + fnClassName + " for function " + fn, e);
 357  126
             }
 358  
 
 359  126
             Object o = c;
 360  
             try
 361  
             {
 362  126
                 if (c != null)
 363  
                 {
 364  126
                     if ("instance".equals(fnType))
 365  
                     {
 366  
                         // instantiate the class
 367  0
                         o = IntrospectionUtils.instantiate(c, args);
 368  
                     }
 369  
                 }
 370  126
                 MethodWrapper mw = new MethodWrapper(o, fnMethName);
 371  126
                 _functionMap.put(fn, mw);
 372  
             }
 373  0
             catch (Exception e)
 374  
             {
 375  0
                 _log.error("Unable to instantiate the function " + fn
 376  
                         + " using the supplied configuration.", e);
 377  126
             }
 378  
         }
 379  
 
 380  6
         MacroIncludeSettingHandler macroHandler = new MacroIncludeSettingHandler();
 381  
 
 382  
         // parse all macro libraries
 383  6
         _config.processListSetting("Macros.Include",macroHandler);
 384  
 
 385  
         // handle exceptions if any
 386  6
         if (macroHandler.e != null) {
 387  0
             throw new InitException("Error loading one or more macro libraries",macroHandler.e);
 388  
         }
 389  
 
 390  6
     }
 391  
 
 392  
     /** This class is necessary as we cannot throw an exception directly from
 393  
      * processSetting(String,String)
 394  
      */
 395  12
     private class MacroIncludeSettingHandler extends Settings.ListSettingHandler {
 396  
         Exception e;
 397  
 
 398  
         public void processSetting(String settingKey, String settingValue) {
 399  
             try {
 400  0
                 Template t = (Template) getProvider("template").get(settingValue);
 401  0
                 _macros.putAll(t.getMacros());
 402  0
             } catch (ResourceException e) {
 403  0
                 _log.error("Error loading macro library '"+settingValue+"', ignoring it",e);
 404  
                 // store exception
 405  0
                 if (this.e == null) this.e = e;
 406  0
             }
 407  0
         }
 408  
 
 409  
     }
 410  
 
 411  
     /* Factory methods -- the supported way of getting a Broker */
 412  
 
 413  
     public static Broker getBroker () throws InitException
 414  
     {
 415  
         try
 416  
         {
 417  850
             Broker b = findBroker(WEBMACRO_PROPERTIES);
 418  850
             if (b == null)
 419  
             {
 420  2
                 b = new Broker();
 421  2
                 register(WEBMACRO_PROPERTIES, b);
 422  
             }
 423  850
             return b;
 424  
         }
 425  0
         catch (InitException e)
 426  
         {
 427  0
             throw new InitException("Failed to initialize WebMacro with default config",e);
 428  
         }
 429  
     }
 430  
 
 431  
     public static Broker getBroker (Properties p) throws InitException
 432  
     {
 433  0
         Broker b = findBroker(p);
 434  0
         if (b == null)
 435  
         {
 436  0
             b = new Broker(p);
 437  0
             register(p, b);
 438  
         }
 439  0
         return b;
 440  
     }
 441  
 
 442  
     public static Broker getBroker (String settingsFile) throws InitException
 443  
     {
 444  0
         Broker b = findBroker(settingsFile);
 445  0
         if (b == null)
 446  
         {
 447  0
             b = new Broker(settingsFile);
 448  0
             register(settingsFile, b);
 449  
         }
 450  
 
 451  0
         return b;
 452  
     }
 453  
 
 454  
     /* Static (internal) methods used for loading settings */
 455  
 
 456  
     protected synchronized void loadDefaultSettings ()
 457  
             throws InitException
 458  
     {
 459  6
         if (_defaultSettings == null)
 460  
         {
 461  
             try
 462  
             {
 463  2
                 _defaultSettings = new Settings(WEBMACRO_DEFAULTS);
 464  
             }
 465  0
             catch (IOException e)
 466  
             {
 467  0
                 throw new InitException("IO Error reading " + WEBMACRO_DEFAULTS,
 468  
                         e);
 469  2
             }
 470  
         }
 471  
         // _log.info("Loading properties file " + WEBMACRO_DEFAULTS);
 472  6
         _config.load(_defaultSettings);
 473  6
     }
 474  
 
 475  
     protected boolean loadSettings (String name,
 476  
                                     boolean optional) throws InitException
 477  
     {
 478  8
         URL u = getResource(name);
 479  8
         if (u != null)
 480  
         {
 481  
             try
 482  
             {
 483  0
                 _config.load(u);
 484  0
                 return true;
 485  
             }
 486  0
             catch (IOException e)
 487  
             {
 488  0
                 if (optional)
 489  
                 {
 490  0
                     _log.info("Cannot find properties file " + name + ", continuing");
 491  
                 }
 492  0
                 e.printStackTrace();
 493  0
                 if (!optional)
 494  
                 {
 495  0
                     throw new InitException("Error reading settings from " + name, e);
 496  
                 }
 497  0
             }
 498  
         }
 499  
         else
 500  
         {
 501  8
             if (!optional)
 502  
             {
 503  0
                 throw new InitException("Error reading settings from " + name);
 504  
             }
 505  
         }
 506  8
         return false;
 507  
     }
 508  
 
 509  
     protected void loadSettings (Properties p)
 510  
     {
 511  0
         _config.load(p);
 512  0
     }
 513  
 
 514  
     protected void loadSystemSettings ()
 515  
     {
 516  
         // _log.info("Loading properties from system properties");
 517  6
         _config.load(System.getProperties(), SETTINGS_PREFIX);
 518  6
     }
 519  
 
 520  
     /**
 521  
      * Used to maintain a weak map mapping the partition _key to the
 522  
      * Broker.  Registers a broker for a given partition _key.
 523  
      */
 524  
     protected static void register (Object key, Broker broker)
 525  
     {
 526  6
         BROKERS.put(key, new WeakReference(broker));
 527  6
     }
 528  
 
 529  
     /**
 530  
      * Find the broker for the specified partition _key, if one is
 531  
      * registered.  Used by factory methods to ensure that there is
 532  
      * only one broker per WM partition
 533  
      */
 534  
     protected static Broker findBroker (Object key)
 535  
     {
 536  882
         WeakReference ref = (WeakReference) BROKERS.get(key);
 537  882
         if (ref != null)
 538  
         {
 539  876
             Broker broker = (Broker) ref.get();
 540  876
             if (broker == null)
 541  0
                 BROKERS.remove(key);
 542  876
             return broker;
 543  
         }
 544  
         else
 545  
         {
 546  6
             return null;
 547  
         }
 548  
     }
 549  
 
 550  
     /**
 551  
      * Access to the settings in WebMacro.properties
 552  
      */
 553  
     public Settings getSettings ()
 554  
     {
 555  1746
         return _config;
 556  
     }
 557  
 
 558  
     /**
 559  
      * Access to the settings in WebMacro.properties
 560  
      */
 561  
     public String getSetting (String key)
 562  
     {
 563  56
         return _config.getSetting(key);
 564  
     }
 565  
 
 566  
     /**
 567  
      * Access to the settings in WebMacro.properties
 568  
      */
 569  
     public boolean getBooleanSetting (String key)
 570  
     {
 571  0
         return _config.getBooleanSetting(key);
 572  
     }
 573  
 
 574  
     /**
 575  
      * Access to the settings in WebMacro.properties
 576  
      */
 577  
     public int getIntegerSetting (String key)
 578  
     {
 579  0
         return _config.getIntegerSetting(key);
 580  
     }
 581  
 
 582  
     /**
 583  
      * Access to the settings in WebMacro.properties
 584  
      */
 585  
     public int getIntegerSetting (String key, int defaultValue)
 586  
     {
 587  0
         return _config.getIntegerSetting(key, defaultValue);
 588  
     }
 589  
 
 590  
 
 591  
     /**
 592  
      * Register a new provider, calling its getType() method to find
 593  
      * out what type of requests it wants to serve.
 594  
      */
 595  
     public void addProvider (Provider p, String pType) throws InitException
 596  
     {
 597  36
         if (pType == null || pType.equals(""))
 598  
         {
 599  0
             pType = p.getType();
 600  
         }
 601  36
         p.init(this, _config);
 602  36
         _providers.put(pType, p);
 603  36
         _log.info("Loaded provider " + p);
 604  36
         if (!pType.equals(p.getType()))
 605  
         {
 606  0
             _log.info("Provider name remapped from " + p.getType()
 607  
                     + " to " + pType);
 608  
         }
 609  36
     }
 610  
 
 611  
     /**
 612  
      * Get a provider
 613  
      */
 614  
     public Provider getProvider (String type) throws NotFoundException
 615  
     {
 616  3990
         Provider p = (Provider) _providers.get(type);
 617  3990
         if (p == null)
 618  
         {
 619  0
             throw new NotFoundException("No provider for type " + type
 620  
                     + ": perhaps WebMacro couldn't load its configuration?");
 621  
         }
 622  3990
         return p;
 623  
     }
 624  
 
 625  
     /**
 626  
      * Get an Iterator of String names that the Provider items in this
 627  
      * Broker have been registered under.  This can then be used with
 628  
      * getProvider(type) to retrieve the registered Provider.
 629  
      * @return Iterator of String
 630  
      **/
 631  
     public Iterator getProviderTypes() 
 632  
     {
 633  0
         return _providers.keySet().iterator();
 634  
     }
 635  
 
 636  
     /**
 637  
      * Close down this Broker.  This will call destroy() on all
 638  
      * registered Providers with the Broker.
 639  
      * @see Provider#destroy()
 640  
      **/
 641  
     public void destroy() 
 642  
     {
 643  0
         Iterator providers = _providers.values().iterator();
 644  0
         while (providers.hasNext()) {
 645  0
             ((Provider) providers.next()).destroy();
 646  
         }
 647  0
     }
 648  
 
 649  
     /**
 650  
      * Get a new FastWriter
 651  
      * A FastWriter is used when writing templates to an output stream
 652  
      *
 653  
      * @param out The output stream the FastWriter should write to.  Typically
 654  
      *           this will be your ServletOutputStream.  It can be null if
 655  
      *           only want the fast writer to buffer the output.
 656  
      * @param enctype the Encoding type to use
 657  
      * @deprecated
 658  
      */
 659  
     public final FastWriter getFastWriter (OutputStream out, String enctype)
 660  
             throws UnsupportedEncodingException
 661  
     {
 662  0
         return FastWriter.getInstance(this, out, enctype);
 663  
     }
 664  
 
 665  
     /**
 666  
      * Get the EvaluationExceptionHandler
 667  
      */
 668  
     public EvaluationExceptionHandler getEvaluationExceptionHandler ()
 669  
     {
 670  30
         return _eeHandler;
 671  
     }
 672  
 
 673  
 
 674  
     /**
 675  
      * Set a new EvaluationExceptionHandler
 676  
      */
 677  
     public void setEvaluationExceptionHandler (EvaluationExceptionHandler eeh)
 678  
     {
 679  0
         _eeHandler = eeh;
 680  0
     }
 681  
 
 682  
 
 683  
     /**
 684  
      * Return a map of all global macros. These macros will be included into
 685  
      * all templates. As the Map implementation is thread-safe, you can even
 686  
      * add macros to the map, if you want to.
 687  
      * @return map of global macros.
 688  
      */
 689  
     public Map getMacros() {
 690  224
         return _macros;
 691  
     }
 692  
     /**
 693  
      * Get a resource (file) from the the Broker's class loader.
 694  
      * We look first with the Broker's class loader, then with the system
 695  
      * class loader, and then for a file.
 696  
      */
 697  
     public URL getResource (String name)
 698  
     {
 699  378
         URL u = _myClassLoader.getResource(name);
 700  378
         if (u == null)
 701  
         {
 702  324
             u = _systemClassLoader.getResource(name);
 703  
         }
 704  378
         if (u == null)
 705  
         {
 706  
             try
 707  
             {
 708  324
                 u = new URL("file", null, -1, name);
 709  324
                 File f = new File(u.getFile());
 710  324
                 if (!f.exists())
 711  
                 {
 712  324
                     u = null;
 713  
                 }
 714  
             }
 715  0
             catch (MalformedURLException ignored)
 716  
             {
 717  0
                 _log.error("MalformedURL", ignored);
 718  324
             }
 719  
         }
 720  378
         return u;
 721  
     }
 722  
 
 723  
     /**
 724  
      * Get a resource (file) from the Broker's class loader
 725  
      */
 726  
     public InputStream getResourceAsStream (String name)
 727  
     {
 728  0
         InputStream is = _myClassLoader.getResourceAsStream(name);
 729  0
         if (is == null)
 730  
         {
 731  0
             is = _systemClassLoader.getResourceAsStream(name);
 732  
         }
 733  0
         if (is == null)
 734  
         {
 735  
             try
 736  
             {
 737  0
                 is = new FileInputStream(name);
 738  
             }
 739  0
             catch (FileNotFoundException ignored)
 740  
             {
 741  0
                 _log.error("FileNotFound", ignored);
 742  0
             }
 743  
         }
 744  0
         return is;
 745  
     }
 746  
 
 747  
     /**
 748  
      * Get a template from the Broker.  By default, this just calls
 749  
      * getResource, but allows brokers to substitute their own idea
 750  
      * of where templates should come from.
 751  
      */
 752  
     public URL getTemplate (String name)
 753  
     {
 754  200
         return getResource(name);
 755  
     }
 756  
 
 757  
 
 758  
     /**
 759  
      * Load a class through the broker's class loader.  Subclasses can
 760  
      * redefine or chain if they know of other ways to load a class.
 761  
      */
 762  
     public Class classForName (String name) throws ClassNotFoundException
 763  
     {
 764  96
         return Class.forName(name);
 765  
     }
 766  
 
 767  
 
 768  
     /**
 769  
      * Look up query against a provider using its integer type handle.
 770  
      */
 771  
     public Object get (String type, final String query)
 772  
             throws ResourceException
 773  
     {
 774  3478
         return getProvider(type).get(query);
 775  
     }
 776  
 
 777  
     /**
 778  
      * Store a _key/value in this Broker.  This is a utility feature for
 779  
      * one to save data that is specific to this instance of WebMacro.<p>
 780  
      *
 781  
      * Please remember that you probably aren't the only one storing keys
 782  
      * in here, so be specific with your _key names.  Don't use names like
 783  
      * <code>String</code> or <code>Foo</code>.  Instead, use
 784  
      * <code>IncludeDirective.String</code> and <code>IncludeDirective.Foo</code>.
 785  
      */
 786  
     public void setBrokerLocal (Object key, Object value)
 787  
     {
 788  6
         _brokerLocal.put(key, value);
 789  6
     }
 790  
 
 791  
     /**
 792  
      * Get a value that was previously stored in this Broker.
 793  
      *
 794  
      * @see #setBrokerLocal
 795  
      */
 796  
     public Object getBrokerLocal (Object key)
 797  
     {
 798  0
         return _brokerLocal.get(key);
 799  
     }
 800  
 
 801  
     /** fetch a "global function" */
 802  
     public MethodWrapper getFunction (String fnName)
 803  
     {
 804  0
         return (MethodWrapper) _functionMap.get(fnName);
 805  
     }
 806  
 
 807  
     /** store a "global function" */
 808  
     public void putFunction (String fnName, MethodWrapper mw)
 809  
     {
 810  0
         _functionMap.put(fnName, mw);
 811  0
     }
 812  
     
 813  
     /** store a "global function" by name */
 814  
     public void putFunction (String fnName, Object instance, String methodName)
 815  
     {
 816  0
         MethodWrapper func = new MethodWrapper(instance, methodName);
 817  0
         putFunction(fnName, func);
 818  0
     }        
 819  
     
 820  
     /** Fetch a tool */
 821  
     public Object getAutoContextVariable(String variableName, Context context) {
 822  12204
         ContextAutoLoader loader = (ContextAutoLoader) _toolLoader.get(variableName);
 823  
         try {
 824  12204
             if (loader == null)
 825  11044
                 return null;
 826  
             else
 827  1160
                 return loader.get(variableName, context);
 828  
         }
 829  0
         catch (PropertyException e) {
 830  0
             return null;
 831  
         }
 832  
     }
 833  
 
 834  
     public void registerAutoContextVariable(String variableName, ContextAutoLoader loader) {
 835  72
         _toolLoader.put(variableName, loader);
 836  72
     }
 837  
 
 838  
     /**
 839  
      * Backwards compatible, calls get(String,String)
 840  
      * @deprecated call get(String,String) instead
 841  
      */
 842  
     public final Object getValue (String type, String query)
 843  
             throws ResourceException
 844  
     {
 845  0
         return get(type, query);
 846  
     }
 847  
 
 848  
     /**
 849  
      * Explain myself
 850  
      */
 851  
     public String toString ()
 852  
     {
 853  0
         StringBuffer buf = new StringBuffer();
 854  0
         buf.append("Broker:");
 855  0
         buf.append(_name);
 856  0
         buf.append("(");
 857  0
         Enumeration e = _providers.elements();
 858  0
         while (e.hasMoreElements())
 859  
         {
 860  0
             Provider pr = (Provider) e.nextElement();
 861  0
             buf.append(pr);
 862  0
             if (e.hasMoreElements())
 863  
             {
 864  0
                 buf.append(", ");
 865  
             }
 866  0
         }
 867  0
         return buf.toString();
 868  
     }
 869  
 
 870  
     public String getName ()
 871  
     {
 872  284
         return _name;
 873  
     }
 874  
 
 875  
     public ClassLoader getClassLoader ()
 876  
     {
 877  0
         return _myClassLoader;
 878  
     }
 879  
 
 880  
     /**
 881  
      * Test the broker or a provider. Reads from stdin: TYPE NAME
 882  
      */
 883  
     public static void main (String[] arg)
 884  
     {
 885  
         try
 886  
         {
 887  0
             if (arg.length != 1)
 888  
             {
 889  0
                 System.out.println("Arg required: config file URL");
 890  0
                 System.out.println("Then input is: TYPE NAME lines on stdin");
 891  0
                 System.exit(1);
 892  
             }
 893  0
             Broker broker = new Broker(arg[0]);
 894  
 
 895  0
             BufferedReader in =
 896  
                     new BufferedReader(new InputStreamReader(System.in));
 897  
 
 898  
             String line;
 899  0
             while ((line = in.readLine()) != null)
 900  
             {
 901  
 
 902  0
                 int space = line.indexOf(' ');
 903  0
                 String type = line.substring(0, space);
 904  0
                 String name = line.substring(space + 1);
 905  0
                 System.out.println("broker.get(\"" + type + "\", \""
 906  
                         + name + "\"):");
 907  0
                 Object o = broker.get(type, name);
 908  0
                 System.out.println("RESULT:");
 909  0
                 System.out.println(o.toString());
 910  0
             }
 911  
         }
 912  0
         catch (Exception e)
 913  
         {
 914  0
             e.printStackTrace();
 915  0
         }
 916  0
     }
 917  
 }