001    package hirondelle.web4j;
002    
003    import hirondelle.web4j.database.ConnectionSource;
004    import hirondelle.web4j.database.ConvertColumn;
005    import hirondelle.web4j.database.ConvertColumnImpl;
006    import hirondelle.web4j.model.AppException;
007    import hirondelle.web4j.model.ConvertParam;
008    import hirondelle.web4j.model.ConvertParamError;
009    import hirondelle.web4j.model.ConvertParamImpl;
010    import hirondelle.web4j.model.ModelCtorException;
011    import hirondelle.web4j.model.ModelCtorUtil;
012    import hirondelle.web4j.request.DateConverter;
013    import hirondelle.web4j.request.LocaleSource;
014    import hirondelle.web4j.request.LocaleSourceImpl;
015    import hirondelle.web4j.request.RequestParser;
016    import hirondelle.web4j.request.RequestParserImpl;
017    import hirondelle.web4j.request.TimeZoneSource;
018    import hirondelle.web4j.request.TimeZoneSourceImpl;
019    import hirondelle.web4j.security.ApplicationFirewall;
020    import hirondelle.web4j.security.ApplicationFirewallImpl;
021    import hirondelle.web4j.security.UntrustedProxyForUserId;
022    import hirondelle.web4j.security.UntrustedProxyForUserIdImpl;
023    import hirondelle.web4j.security.PermittedCharacters;
024    import hirondelle.web4j.security.PermittedCharactersImpl;
025    import hirondelle.web4j.security.SpamDetector;
026    import hirondelle.web4j.security.SpamDetectorImpl;
027    import hirondelle.web4j.ui.translate.Translator;
028    import hirondelle.web4j.util.TimeSource;
029    import hirondelle.web4j.util.TimeSourceImpl;
030    import hirondelle.web4j.util.Util;
031    import hirondelle.web4j.webmaster.Emailer;
032    import hirondelle.web4j.webmaster.EmailerImpl;
033    import hirondelle.web4j.webmaster.LoggingConfig;
034    import hirondelle.web4j.webmaster.LoggingConfigImpl;
035    import java.lang.reflect.Constructor;
036    import java.util.Enumeration;
037    import java.util.LinkedHashMap;
038    import java.util.List;
039    import java.util.Map;
040    import java.util.logging.Logger;
041    import javax.servlet.ServletConfig;
042    
043    /**
044     Return concrete instances of configured implementation classes.
045     
046     <P>WEB4J requires the application programmer to supply concrete implementations of a number of  
047     interfaces and a single abstract class. <tt>BuildImpl</tt> returns instances of those abstractions. Over half 
048     of these items have default implementations, which can be used by the application programmer 
049     without any configuration effort at all.
050     
051     <P>When the framework needs a specific implementation, it uses the services of this class. 
052     (If the application programmer needs to refer to such an implementation, they have the option of using 
053     the methods in this class, instead of referring directly to their implementation.)
054    
055     <P><h3>Configuration Styles</h3>
056     Concrete implementation classes can be configured in three ways :
057     <ul>
058     <li>do nothing at all. In this case, a default implementation defined by <tt>WEB4J</tt> will be used. 
059     Several of the WEB4J abstractions (such as {@link ConnectionSource}) do 
060     not have a default implementation, so this style of configuration is not always possible. 
061     <li>implement a concrete class <em>of a conventional package and name</em>. The conventional <em>package</em> name 
062     is always '<tt>hirondelle.web4j.config</tt>', while the conventional <em>class</em> name varies - 
063     see <a href="#Listing">below</a>. 
064     <li>implement a concrete class of a <em>non-conventional</em> package and name, 
065     and add an <tt>init-param</tt> setting to <tt>web.xml</tt> of the form :
066     <PRE>
067      &lt;init-param&gt;
068       &lt;param-name&gt;ImplementationFor.hirondelle.web4j.ApplicationInfo&lt;/param-name&gt;
069       &lt;param-value&gt;com.xyz.MyAppInfo&lt;/param-value&gt;
070       &lt;description&gt;
071         Package-qualified name of class describing simple, 
072         high level information about this application. 
073       &lt;/description&gt;
074      &lt;/init-param&gt; 
075     </PRE>
076     </ul>
077     
078     <P>The {@link #init(ServletConfig)} method will look for implementations in the reverse of the above order. 
079     That is, 
080    <ol>
081     <li>an <em>explicit</em> <tt>ImplementationFor.*</tt> setting in <tt>web.xml</tt>
082     <li>a class of a <em>conventional</em> package and name
083     <li>the <em>default</em> WEB4J implementation (if a default implementation exists)
084    </ol>
085     
086     <P><a name="Listing"></a><h3>Listing of Interfaces and Conventional Names</h3>
087     Here is a listing of all interfaces used by WEB4J, along with conventional class names, and either a default 
088     or example implementation. The package for conventional class names is always '<tt>hirondelle.web4j.config</tt>'.
089     
090     <P><table BORDER CELLSPACING=0 CELLPADDING=3 >
091     <tr>
092     <th>Question</th>
093     <th>Interface</th>
094     <th>Conventional Impl Name, in <tt>hirondelle.web4j.config</tt></th>
095     <th>Default/Example Implementation</th>
096     </tr>
097     <tr valign="top">
098     <td>What is the application's name, version, build date, and so on?</td>
099     <td>{@link hirondelle.web4j.ApplicationInfo}</td>
100     <td><tt>AppInfo</tt></td>
101     <td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/AppInfo.html">example</a></td>
102     </tr>
103     <tr valign="top">
104     <td>What tasks need to be performed during startup?</td>
105     <td>{@link hirondelle.web4j.StartupTasks}</td>
106     <td><tt>Startup</tt></td>
107     <td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/Startup.html">example</a></td>
108     </tr>
109     <tr valign="top">
110     <td>What <tt>Action</tt> is related to each request?</td>
111     <td>{@link hirondelle.web4j.request.RequestParser} (an ABC)</td>
112     <td><tt>RequestToAction</tt></td>
113     <td>{@link hirondelle.web4j.request.RequestParserImpl}</a></td>
114     </tr>
115     <tr valign="top">
116     <td>Which requests should be treated as malicious attacks?</td>
117     <td>{@link hirondelle.web4j.security.ApplicationFirewall}</td>
118     <td><tt>AppFirewall</tt></td>
119     <td>{@link hirondelle.web4j.security.ApplicationFirewallImpl}</td>
120     </tr>
121     <tr valign="top">
122     <td>Which requests use untrusted proxies for the user id?</td>
123     <td>{@link hirondelle.web4j.security.UntrustedProxyForUserId}</td>
124     <td><tt>OwnerFirewall</tt></td>
125     <td>{@link hirondelle.web4j.security.UntrustedProxyForUserIdImpl}</td>
126     </tr>
127     <tr valign="top">
128     <td>How is spam distinguished from regular user input?</td>
129     <td>{@link hirondelle.web4j.security.SpamDetector}</td>
130     <td><tt>SpamDetect</tt></td>
131     <td>{@link hirondelle.web4j.security.SpamDetectorImpl}</td>
132     </tr>
133     <tr valign="top">
134     <td>How is a request param translated into a given target type?</td>
135     <td>{@link hirondelle.web4j.model.ConvertParam}</td>
136     <td><tt>ConvertParams</tt></td>
137     <td>{@link hirondelle.web4j.model.ConvertParamImpl}</td>
138     </tr>
139     <tr valign="top">
140     <td>How does the application respond when a low level conversion error takes place when parsing user input?</td>
141     <td>{@link hirondelle.web4j.model.ConvertParamError}</td>
142     <td><tt>ConvertParamErrorImpl</tt></td>
143     <td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/ConvertParamErrorImpl.html">example</a></td>
144     </tr>
145     <tr valign="top">
146     <td>What characters are permitted for text input fields?</td>
147     <td>{@link hirondelle.web4j.security.PermittedCharacters}</td>
148     <td><tt>PermittedChars</tt></td>
149     <td>{@link hirondelle.web4j.security.PermittedCharactersImpl}</td>
150     </tr>
151     <tr valign="top">
152     <td>How is a date formatted and parsed?</td>
153     <td>{@link hirondelle.web4j.request.DateConverter}</td>
154     <td><tt>DateConverterImpl</tt></td>
155     <td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/DateConverterImpl.html">example</a></td>
156     </tr>
157     <tr valign="top">
158     <td>How is a <tt>Locale</tt> derived from the request?</td>
159     <td>{@link hirondelle.web4j.request.LocaleSource}</td>
160     <td><tt>LocaleSrc</tt></td>
161     <td>{@link hirondelle.web4j.request.LocaleSourceImpl}</td>
162     </tr>
163     <tr valign="top">
164     <td>How is the system clock defined?</td>
165     <td>{@link hirondelle.web4j.util.TimeSource}</td>
166     <td><tt>TimeSrc</tt></td>
167     <td>{@link hirondelle.web4j.util.TimeSourceImpl}</td>
168     </tr>
169     <tr valign="top">
170     <td>How is a <tt>TimeZone</tt> derived from the request?</td>
171     <td>{@link hirondelle.web4j.request.TimeZoneSource}</td>
172     <td><tt>TimeZoneSrc</tt></td>
173     <td>{@link hirondelle.web4j.request.TimeZoneSourceImpl}</td>
174     </tr>
175     <tr valign="top">
176     <td>What is the translation of this text, for a given <tt>Locale</tt>?</td>
177     <td>{@link hirondelle.web4j.ui.translate.Translator}</td>
178     <td><tt>TranslatorImpl</tt></td>
179     <td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/TranslatorImpl.html">example</a></td>
180     </tr>
181     <tr valign="top">
182     <td>How does the application obtain a database <tt>Connection</tt>?</td>
183     <td>{@link hirondelle.web4j.database.ConnectionSource}</td>
184     <td><tt>ConnectionSrc</tt></td>
185     <td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/ConnectionSrc.html">example</a></td>
186     </tr>
187     <tr valign="top">
188     <td>How is a <tt>ResultSet</tt> column translated into a given target type?</td>
189     <td>{@link hirondelle.web4j.database.ConvertColumn}</td>
190     <td><tt>ColToObject</tt></td>
191     <td>{@link hirondelle.web4j.database.ConvertColumnImpl}</td>
192     </tr>
193     <tr valign="top">
194     <td>How should an email be sent when a problem occurs?</td>
195     <td>{@link hirondelle.web4j.webmaster.Emailer}</td>
196    <td><tt>Email</tt></td>
197     <td>{@link hirondelle.web4j.webmaster.EmailerImpl}</td>
198     </tr>
199     <tr valign="top">
200     <td>How should the logging system be configured?</td>
201     <td>{@link hirondelle.web4j.webmaster.LoggingConfig}</td>
202     <td><tt>LogConfig</tt></td>
203     <td>{@link hirondelle.web4j.webmaster.LoggingConfigImpl}</td>
204     </tr>
205     <tr valign="top">
206     <td>Does this request/operation have a data ownership constraint?</td>
207     <td>{@link hirondelle.web4j.security.UntrustedProxyForUserId}</td>
208     <td><tt>OwnerFirewall</tt></td>
209     <td>{@link hirondelle.web4j.security.UntrustedProxyForUserIdImpl}</td>
210     </tr>
211     </table>
212     
213     <p><span class="highlight">No conflict between the classes of different
214     applications will result, if application code is placed in the usual locations
215     under <tt>WEB-INF</span></tt>, and not in <i>shared</i> locations accessible
216     to multiple web applications. Since version 2.2 of the Servlet API, each
217     web application gets its own {@link java.lang.ClassLoader}, so no conflict
218     will result, as long as classes are placed in non-shared locations (which
219     is almost always the case).
220    
221     <P>This class does not cache objects in any way.
222    */
223    public final class BuildImpl {
224      
225      /*
226       Note: some might make better use of generics here. BUT from the point of view of the 
227       caller, the forXXX methods should remain. That way the caller does not have to 
228       remember the interface class literal. (As well, the web.xml settings for 
229       intf/impl are text, not class literals.)
230      */
231      
232      /**
233       Called by the framework upon startup. 
234       
235       <P>Extract all configuration which maps names of abstractions to names of corresponding  
236       concrete implementations. Confirm both that all required interfaces 
237       have configured implementations, and that they can be loaded.
238       
239       <P>The implementation of {@link TimeSource} and {@link LoggingConfig} are treated slightly 
240       differently than the rest. Their implementations are found earlier than the others, since they 
241       are of immediate use. 
242       
243       <P>See class comment for more information.
244      */
245      public static void init(ServletConfig aConfig) throws AppException {
246        useWebXmlSettingsFirst(aConfig);
247        doTimeSourceConfig();
248        doLoggingConfig(aConfig);
249        fLogger.config("________________________ STARTUP :Initializing WEB4J Controller. Reading in settings in web.xml._________");
250        useStandardOrDefaultNameSecond();
251        fLogger.config("Mapping of implementation classes : " + Util.logOnePerLine(fClassMapping));
252      }
253    
254      /**
255       Map a fully-qualified <tt>aAbstractionName</tt> into a concrete implementation.
256      
257       <P>This method should only be used for 'non-standard' items not covered by more  
258       specific methods in this class. For example, when looking for the implementation of {@link LocaleSource},
259       the {@link #forLocaleSource()} method should always be used instead of this method.
260       
261       <P>Implementation classes accessed by this method must have a <tt>public</tt> no-argument 
262       constructor. (This method is best suited for interfaces, and not abstract base classes.)
263       
264       <P>Uses {@link Class#newInstance}, with no arguments. If a problem occurs, a 
265       {@link RuntimeException} is thrown. 
266      
267       @param aAbstractionName package-qualified name of an interface or abstract base class, as in 
268       "<tt>hirondelle.web4j.ApplicationInfo</tt>".
269      */
270      public static Object forAbstraction(String aAbstractionName){
271        Class<?> implementationClass = fClassMapping.get(aAbstractionName);
272        if ( implementationClass == null )  {
273          throw new IllegalArgumentException(
274            "No mapping to an implementation class found, for interface or abstract base class named " + Util.quote(aAbstractionName)
275          );
276        }
277        Object result = null;
278        try {
279          result = implementationClass.newInstance();
280        }
281        catch (InstantiationException ex){
282          handleCtorProblem(ex, implementationClass);
283        }
284        catch (IllegalAccessException ex) {
285          handleCtorProblem(ex, implementationClass);
286        }
287        return result;
288      }
289      
290      /**
291       Map a fully-qualified <tt>aAbstractBaseClassName</tt> into a concrete implementation.
292      
293       <P>Intended for abstract base classes (ABC's) having a <tt>public</tt> 
294       constructor with known arguments. For example, this method is used by 
295       the {@link hirondelle.web4j.Controller} to build an implementation of 
296       {@link hirondelle.web4j.request.RequestParser}, by passing in a <tt>request</tt> 
297       and <tt>response</tt> object. (Implementations of that ABC are always expected to 
298       take those two particular constructor arguments.)
299       
300       <P>If a problem occurs, a {@link RuntimeException} is thrown.
301       
302       @param aAbstractBaseClassName package-qualified name of an Abstract Base Class, as in 
303       "<tt>hirondelle.web4j.ui.RequestParser</tt>".
304       @param aCtorArguments <tt>List</tt> of arguments to be passed to the constructor of an  
305       implementation class; the size of this list determines the selected constructor (by 
306       matching the number of parameters), and the iteration order of its items corresponds 
307       to the order of appearance of the formal constructor parameters.
308      */
309      public static Object forAbstractionPassCtorArgs(String aAbstractBaseClassName, List<Object> aCtorArguments){
310        Object result = null;
311        Class implClass = fClassMapping.get(aAbstractBaseClassName);
312        Constructor ctor = ModelCtorUtil.getConstructor(implClass, aCtorArguments.size());
313        try {
314          result = ModelCtorUtil.buildModelObject(ctor, aCtorArguments);
315        }
316        catch (ModelCtorException ex){
317          handleCtorProblem(ex, implClass);
318        }
319        return result;
320      }
321      
322      /** Return the configured implementation of {@link ApplicationInfo}.  */
323      public static ApplicationInfo forApplicationInfo(){
324        return (ApplicationInfo)forAbstraction(APPLICATION_INFO.getAbstraction());
325      }
326      
327      /** Return the configured implementation of {@link StartupTasks}.  */
328      public static StartupTasks forStartupTasks(){
329        return (StartupTasks)forAbstraction(STARTUP_TASKS.getAbstraction());
330      }
331      
332      /** Return the configured implementation of {@link ConvertParamError}.  */
333      public static ConvertParamError forConvertParamError(){
334        return (ConvertParamError)forAbstraction(CONVERT_PARAM_ERROR.getAbstraction());
335      }
336      
337      /** Return the configured implementation of {@link ConvertColumn}.  */
338      public static ConvertColumn forConvertColumn(){
339        return (ConvertColumn)forAbstraction(CONVERT_COLUMN.getAbstraction());
340      }
341    
342      /** Return the configured implementation of {@link PermittedCharacters}.  */
343      public static PermittedCharacters forPermittedCharacters(){
344        return (PermittedCharacters)forAbstraction(PERMITTED_CHARACTERS.getAbstraction());
345      }
346      
347      /** Return the configured implementation of {@link ConnectionSource}.  */
348      public static ConnectionSource forConnectionSource(){
349        return (ConnectionSource)forAbstraction(CONNECTION_SOURCE.getAbstraction());    
350      }
351      
352      /** Return the configured implementation of {@link LocaleSource}.  */
353      public static LocaleSource forLocaleSource(){
354        return (LocaleSource)forAbstraction(LOCALE_SRC.getAbstraction());
355      }
356    
357      /** 
358      Return the configured implementation of {@link TimeSource}.
359      
360      <P>When testing, an application may call this method in order to use a 'fake'
361      system time.
362      
363       <P>Internally, WEB4J will always use this method when it needs the current time.
364       This allows a fake system time to be shared between your application and WEB4J.
365      */
366      public static TimeSource forTimeSource(){
367        return (TimeSource)forAbstraction(TIME_SRC.getAbstraction());
368      }
369      
370      /** Return the configured implementation of {@link TimeZoneSource}.  */
371      public static TimeZoneSource forTimeZoneSource(){
372        return (TimeZoneSource)forAbstraction(TIME_ZONE_SRC.getAbstraction());
373      }
374      
375      /** Return the configured implementation of {@link DateConverter}.  */
376      public static DateConverter forDateConverter(){
377        return (DateConverter)forAbstraction(DATE_CONVERTER.getAbstraction());
378      }
379      
380      /** Return the configured implementation of {@link Translator}.  */
381      public static Translator forTranslator(){
382        return (Translator)forAbstraction(TRANSLATOR.getAbstraction());
383      }
384      
385      /** Return the configured implementation of {@link ApplicationFirewall}.  */
386      public static ApplicationFirewall forApplicationFirewall() {
387        return (ApplicationFirewall)forAbstraction(APP_FIREWALL.getAbstraction());
388      }
389      
390      /** Return the configured implementation of {@link SpamDetector}.  */
391      public static SpamDetector forSpamDetector() {
392        return (SpamDetector)forAbstraction(SPAM_DETECTOR.getAbstraction());
393      }
394      
395      /** Return the configured implementation of {@link Emailer}.  */
396      public static Emailer forEmailer() {
397        return (Emailer)forAbstraction(EMAILER.getAbstraction());
398      }
399      
400      /** Return the configured implementation of {@link ConvertParam}.  */
401      public static ConvertParam forConvertParam() {
402        return (ConvertParam)forAbstraction(CONVERT_PARAM.getAbstraction());
403      }
404      
405      /** Return the configured implementation of {@link UntrustedProxyForUserId}.  */
406      public static UntrustedProxyForUserId forOwnershipFirewall() {
407        return (UntrustedProxyForUserId)forAbstraction(OWNER_FIREWALL.getAbstraction());
408      }
409    
410      /**
411        Add an implementation - intended for testing only.
412          
413         <P>This method allows testing code to configure a specific implementation class. 
414         Example: <PRE>BuildImpl.adHocImplementationAdd(TimeSource.class, MyTimeSource.class);</PRE>
415         Calls to this method (often in a JUnit <tt>setUp()</tt> method) should be paired with a 
416         subsequent call to {@link #adHocImplementationRemove(Class)}. 
417      */
418      public static void adHocImplementationAdd(Class aInterface, Class aImplementationClass){
419         fClassMapping.put(aInterface.getName(), aImplementationClass);
420      }
421      
422      /**
423        Remove an implementation - intended for testing only.
424         
425        <P>This method allows testing code to configure a specific implementation class. 
426        Example: <PRE>BuildImpl.adHocImplementationRemove(TimeSource.class);</PRE> 
427        Calls to this method (often in a JUnit <tt>tearDown()</tt> method) should be paired with 
428        a previous call to {@link #adHocImplementationAdd(Class, Class)}. 
429      */
430      public static void adHocImplementationRemove(Class aInterface){
431        fClassMapping.remove(aInterface.getName());
432      }
433      
434      // PRIVATE 
435      
436      /**
437       Key - interface name (String)
438       Value - implementation class (Class) 
439      */
440      private static final Map<String, Class<?>> fClassMapping = new LinkedHashMap<String, Class<?>>();
441      
442      private static final String IMPLEMENTATION_FOR = "ImplementationFor.";
443      private static final Logger fLogger = Util.getLogger(BuildImpl.class);
444      
445      private BuildImpl(){
446        //prevent construction by caller
447      }
448      
449      /*
450       Implementation Note. 
451       Early versions of this class did not work with class literals. Problem disappeared?
452      */
453      
454      private static final String STANDARD_PACKAGE = "hirondelle.web4j.config.";
455      
456      //Items with no WEB4J default
457      private static final StandardDefault APPLICATION_INFO = new StandardDefault(ApplicationInfo.class.getName(),"AppInfo");
458      private static final StandardDefault STARTUP_TASKS = new StandardDefault(StartupTasks.class.getName(), "Startup");
459      private static final StandardDefault CONNECTION_SOURCE = new StandardDefault(ConnectionSource.class.getName(), "ConnectionSrc");
460      private static final StandardDefault CONVERT_PARAM_ERROR = new StandardDefault(ConvertParamError.class.getName(), "ConvertParamErrorImpl");
461      private static final StandardDefault TRANSLATOR = new StandardDefault(Translator.class.getName(), "TranslatorImpl");
462      private static final StandardDefault DATE_CONVERTER = new StandardDefault(DateConverter.class.getName(), "DateConverterImpl");
463      
464      //Items with a WEB4J default
465      private static final StandardDefault LOGGING_CONFIG = new StandardDefault(LoggingConfig.class.getName(), "LogConfig", LoggingConfigImpl.class.getName());
466      private static final StandardDefault REQUEST_PARSER = new StandardDefault(RequestParser.class.getName(), "RequestToAction", RequestParserImpl.class.getName());
467      private static final StandardDefault APP_FIREWALL = new StandardDefault(ApplicationFirewall.class.getName(), "AppFirewall", ApplicationFirewallImpl.class.getName());
468      private static final StandardDefault CONVERT_COLUMN = new StandardDefault(ConvertColumn.class.getName(), "ConvertColumns", ConvertColumnImpl.class.getName());
469      private static final StandardDefault LOCALE_SRC = new StandardDefault(LocaleSource.class.getName(), "LocaleSrc", LocaleSourceImpl.class.getName());
470      private static final StandardDefault TIME_SRC = new StandardDefault(TimeSource.class.getName(), "TimeSrc", TimeSourceImpl.class.getName());
471      private static final StandardDefault TIME_ZONE_SRC = new StandardDefault(TimeZoneSource.class.getName(), "TimeZoneSrc", TimeZoneSourceImpl.class.getName());
472      private static final StandardDefault SPAM_DETECTOR = new StandardDefault(SpamDetector.class.getName(), "SpamDetect", SpamDetectorImpl.class.getName());
473      private static final StandardDefault EMAILER = new StandardDefault(Emailer.class.getName(), "Email", EmailerImpl.class.getName());
474      private static final StandardDefault CONVERT_PARAM = new StandardDefault(ConvertParam.class.getName(), "ConvertParams", ConvertParamImpl.class.getName());
475      private static final StandardDefault PERMITTED_CHARACTERS = new StandardDefault(PermittedCharacters.class.getName(), "PermittedChars", PermittedCharactersImpl.class.getName());
476      private static final StandardDefault OWNER_FIREWALL = new StandardDefault(UntrustedProxyForUserId.class.getName(), "OwnerFirewall", UntrustedProxyForUserIdImpl.class.getName());
477      
478      //OTHERS? MUST add below as well...
479    
480      private static void useWebXmlSettingsFirst(ServletConfig aConfig) throws AppException {
481        Enumeration params = aConfig.getInitParameterNames();
482        while ( params.hasMoreElements() ){
483          String paramName = (String)params.nextElement();
484          if ( paramName.startsWith(IMPLEMENTATION_FOR) ) {
485            String interfaceName = paramName.substring(IMPLEMENTATION_FOR.length());
486            String className = aConfig.getInitParameter(paramName);
487            fClassMapping.put(interfaceName, buildWebXmlClass(className));
488          }
489        }
490      }
491      
492      private static void handleCtorProblem(Exception ex, Class<?> aImplementationClass){
493        String message = "Object construction by reflection failed for " + aImplementationClass.toString();
494        fLogger.severe(message);
495        throw new RuntimeException(message, ex);
496      }
497      
498      /** The system time must be done first, since used everywhere, including the logging system. */
499      private static void doTimeSourceConfig() throws AppException {
500        addStandardDefaultIfNotInWebXml(TIME_SRC); 
501      }
502      
503      /** Extract and execute the LoggingConfig, earlier than all others.  */
504      private static void doLoggingConfig(ServletConfig aConfig) throws AppException {
505        addStandardDefaultIfNotInWebXml(LOGGING_CONFIG); 
506        executeLoggingConfig(aConfig);
507      }
508      
509      private static void useStandardOrDefaultNameSecond() throws AppException {
510        fLogger.config("For items *not* specified in web.xml, searching for implementations with 'standard' name.");
511        fLogger.config("If no 'standard' implementation found, then will use the WEB4J 'default' implementation.");
512        //does NOT include the items done 'early'
513        addStandardDefaultIfNotInWebXml(APPLICATION_INFO);
514        addStandardDefaultIfNotInWebXml(CONNECTION_SOURCE);
515        addStandardDefaultIfNotInWebXml(CONVERT_PARAM_ERROR);
516        addStandardDefaultIfNotInWebXml(TRANSLATOR);
517        addStandardDefaultIfNotInWebXml(DATE_CONVERTER);
518        addStandardDefaultIfNotInWebXml(STARTUP_TASKS);
519        addStandardDefaultIfNotInWebXml(REQUEST_PARSER);
520        addStandardDefaultIfNotInWebXml(APP_FIREWALL);
521        addStandardDefaultIfNotInWebXml(CONVERT_COLUMN);
522        addStandardDefaultIfNotInWebXml(LOCALE_SRC);
523        addStandardDefaultIfNotInWebXml(TIME_ZONE_SRC);
524        addStandardDefaultIfNotInWebXml(SPAM_DETECTOR);
525        addStandardDefaultIfNotInWebXml(EMAILER);
526        addStandardDefaultIfNotInWebXml(CONVERT_PARAM);
527        addStandardDefaultIfNotInWebXml(PERMITTED_CHARACTERS);
528        addStandardDefaultIfNotInWebXml(OWNER_FIREWALL);
529      }
530      
531      private static boolean isAlreadySpecified(String aInterfaceName){
532        return fClassMapping.keySet().contains(aInterfaceName);
533      }
534    
535      private static Class<?> buildWebXmlClass(String aClassName) throws AppException {
536        Class<?> result = null;
537        try {
538          result = Class.forName(aClassName);
539        }
540        catch (ClassNotFoundException ex){
541          throw new AppException(
542            "Load of configured (or default) implementation class has failed. Class.forName() failed for " + Util.quote(aClassName), ex
543          );
544        }
545        return result;
546      }
547    
548      private static void addStandardDefaultIfNotInWebXml(StandardDefault aNames) throws AppException {
549        if ( ! isAlreadySpecified(aNames.getAbstraction()) ){
550          fClassMapping.put(aNames.getAbstraction(), buildStandardOrDefaultClass(aNames.getStandard(), aNames.getDefault()));
551        }
552      }
553      
554      private static Class<?> buildStandardOrDefaultClass(String aStandardName, String aDefaultName) throws AppException {
555        Class<?> result = null;
556        try {
557          result = Class.forName(aStandardName);
558        }
559        catch (ClassNotFoundException ex){
560          if( ! Util.textHasContent(aDefaultName) ){
561            throw new AppException(
562              "Load of configured implementation class has failed. Class.forName() failed for " + Util.quote(aStandardName), ex
563            );
564          }
565          fLogger.config("Cannot see any class named " + Util.quote(aStandardName) + ". Will use default WEB4J implementation instead, named " + Util.quote(aDefaultName));
566          try {
567            result = Class.forName(aDefaultName);
568          }
569          catch (ClassNotFoundException exception){
570            throw new AppException(
571              "Load of default implementation has failed. Class.forName() failed for " + Util.quote(aDefaultName), exception
572            );
573          }
574        }
575        return result;
576      }
577    
578      /** Execute the configured implementation of {@link LoggingConfig}.  */
579      private static void executeLoggingConfig(ServletConfig aConfig) throws AppException {
580        LoggingConfig loggingConfig = forLoggingConfig();
581        loggingConfig.setup(aConfig);
582      }
583      
584      /**  Return the configured implementation of {@link LoggingConfig}.  */ 
585      private static LoggingConfig forLoggingConfig(){
586        return (LoggingConfig)forAbstraction(LOGGING_CONFIG.getAbstraction());
587      }
588      
589      /** Gathers the standard and default class names related to an abstraction.   */
590      private static final class StandardDefault {
591        StandardDefault(String aAbstraction, String aStandard){
592          fAbstraction = aAbstraction;
593          fStandard = STANDARD_PACKAGE + aStandard;
594        }
595        StandardDefault(String aAbstraction, String aStandard, String aDefault){
596          fAbstraction = aAbstraction;
597          fStandard = STANDARD_PACKAGE + aStandard;
598          fDefault = aDefault;
599        }
600        boolean hasDefault(){
601          return Util.textHasContent(fDefault);
602        }
603        String getAbstraction() {  return fAbstraction;    }
604        String getDefault() {   return fDefault;   }
605        String getStandard() {    return fStandard;   }
606        private String fAbstraction;
607        private String fStandard;
608        private String fDefault;
609      }
610    }