Package hirondelle.web4j

Source Code of hirondelle.web4j.BuildImpl

package hirondelle.web4j;

import hirondelle.web4j.database.ConnectionSource;
import hirondelle.web4j.database.ConvertColumn;
import hirondelle.web4j.database.ConvertColumnImpl;
import hirondelle.web4j.model.AppException;
import hirondelle.web4j.model.ConvertParam;
import hirondelle.web4j.model.ConvertParamError;
import hirondelle.web4j.model.ConvertParamImpl;
import hirondelle.web4j.model.ModelCtorException;
import hirondelle.web4j.model.ModelCtorUtil;
import hirondelle.web4j.request.DateConverter;
import hirondelle.web4j.request.LocaleSource;
import hirondelle.web4j.request.LocaleSourceImpl;
import hirondelle.web4j.request.RequestParser;
import hirondelle.web4j.request.RequestParserImpl;
import hirondelle.web4j.request.TimeZoneSource;
import hirondelle.web4j.request.TimeZoneSourceImpl;
import hirondelle.web4j.security.ApplicationFirewall;
import hirondelle.web4j.security.ApplicationFirewallImpl;
import hirondelle.web4j.security.LoginTasks;
import hirondelle.web4j.security.PermittedCharacters;
import hirondelle.web4j.security.PermittedCharactersImpl;
import hirondelle.web4j.security.SpamDetector;
import hirondelle.web4j.security.SpamDetectorImpl;
import hirondelle.web4j.security.UntrustedProxyForUserId;
import hirondelle.web4j.security.UntrustedProxyForUserIdImpl;
import hirondelle.web4j.ui.translate.Translator;
import hirondelle.web4j.util.TimeSource;
import hirondelle.web4j.util.TimeSourceImpl;
import hirondelle.web4j.util.Util;
import hirondelle.web4j.webmaster.Emailer;
import hirondelle.web4j.webmaster.EmailerImpl;
import hirondelle.web4j.webmaster.LoggingConfig;
import hirondelle.web4j.webmaster.LoggingConfigImpl;

import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.servlet.ServletConfig;

/**
Return concrete instances of configured implementation classes.
<P>WEB4J requires the application programmer to supply concrete implementations of a number of 
interfaces and a single abstract class. <tt>BuildImpl</tt> returns instances of those abstractions. Over half
of these items have default implementations, which can be used by the application programmer
without any configuration effort at all.
<P>When the framework needs a specific implementation, it uses the services of this class.
(If the application programmer needs to refer to such an implementation, they have the option of using
the methods in this class, instead of referring directly to their implementation.)

<P><h3>Configuration Styles</h3>
Concrete implementation classes can be configured in three ways :
<ul>
<li>do nothing at all. In this case, a default implementation defined by <tt>WEB4J</tt> will be used.
Several of the WEB4J abstractions (such as {@link ConnectionSource}) do
not have a default implementation, so this style of configuration is not always possible.
<li>implement a concrete class <em>of a conventional package and name</em>. The conventional <em>package</em> name
is always '<tt>hirondelle.web4j.config</tt>', while the conventional <em>class</em> name varies -
see <a href="#Listing">below</a>.
<li>implement a concrete class of a <em>non-conventional</em> package and name,
and add an <tt>init-param</tt> setting to <tt>web.xml</tt> of the form :
<PRE>
  &lt;init-param&gt;
   &lt;param-name&gt;ImplementationFor.hirondelle.web4j.ApplicationInfo&lt;/param-name&gt;
   &lt;param-value&gt;com.xyz.MyAppInfo&lt;/param-value&gt;
   &lt;description&gt;
     Package-qualified name of class describing simple,
     high level information about this application.
   &lt;/description&gt;
  &lt;/init-param&gt;
</PRE>
</ul>
<P>The {@link #init(ServletConfig)} method will look for implementations in the reverse of the above order.
That is,
<ol>
<li>an <em>explicit</em> <tt>ImplementationFor.*</tt> setting in <tt>web.xml</tt>
<li>a class of a <em>conventional</em> package and name
<li>the <em>default</em> WEB4J implementation (if a default implementation exists)
</ol>
<P><a name="Listing"></a><h3>Listing of Interfaces and Conventional Names</h3>
Here is a listing of all interfaces used by WEB4J, along with conventional class names, and either a default
or example implementation. The package for conventional class names is always '<tt>hirondelle.web4j.config</tt>'.
<P><table BORDER CELLSPACING=0 CELLPADDING=3 >
<tr>
<th>Question</th>
<th>Interface</th>
<th>Conventional Impl Name, in <tt>hirondelle.web4j.config</tt></th>
<th>Default/Example Implementation</th>
</tr>
<tr valign="top">
<td>What is the application's name, version, build date, and so on?</td>
<td>{@link hirondelle.web4j.ApplicationInfo}</td>
<td><tt>AppInfo</tt></td>
<td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/AppInfo.html">example</a></td>
</tr>
<tr valign="top">
<td>What tasks need to be performed during startup?</td>
<td>{@link hirondelle.web4j.StartupTasks}</td>
<td><tt>Startup</tt></td>
<td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/Startup.html">example</a></td>
</tr>
<tr valign="top">
<td>What tasks need to be performed after user login?</td>
<td>{@link hirondelle.web4j.security.LoginTasks}</td>
<td><tt>Login</tt></td>
<td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/Login.html">example</a></td>
</tr>
<tr valign="top">
<td>What <tt>Action</tt> is related to each request?</td>
<td>{@link hirondelle.web4j.request.RequestParser} (an ABC)</td>
<td><tt>RequestToAction</tt></td>
<td>{@link hirondelle.web4j.request.RequestParserImpl}</a></td>
</tr>
<tr valign="top">
<td>Which requests should be treated as malicious attacks?</td>
<td>{@link hirondelle.web4j.security.ApplicationFirewall}</td>
<td><tt>AppFirewall</tt></td>
<td>{@link hirondelle.web4j.security.ApplicationFirewallImpl}</td>
</tr>
<tr valign="top">
<td>Which requests use untrusted proxies for the user id?</td>
<td>{@link hirondelle.web4j.security.UntrustedProxyForUserId}</td>
<td><tt>OwnerFirewall</tt></td>
<td>{@link hirondelle.web4j.security.UntrustedProxyForUserIdImpl}</td>
</tr>
<tr valign="top">
<td>How is spam distinguished from regular user input?</td>
<td>{@link hirondelle.web4j.security.SpamDetector}</td>
<td><tt>SpamDetect</tt></td>
<td>{@link hirondelle.web4j.security.SpamDetectorImpl}</td>
</tr>
<tr valign="top">
<td>How is a request param translated into a given target type?</td>
<td>{@link hirondelle.web4j.model.ConvertParam}</td>
<td><tt>ConvertParams</tt></td>
<td>{@link hirondelle.web4j.model.ConvertParamImpl}</td>
</tr>
<tr valign="top">
<td>How does the application respond when a low level conversion error takes place when parsing user input?</td>
<td>{@link hirondelle.web4j.model.ConvertParamError}</td>
<td><tt>ConvertParamErrorImpl</tt></td>
<td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/ConvertParamErrorImpl.html">example</a></td>
</tr>
<tr valign="top">
<td>What characters are permitted for text input fields?</td>
<td>{@link hirondelle.web4j.security.PermittedCharacters}</td>
<td><tt>PermittedChars</tt></td>
<td>{@link hirondelle.web4j.security.PermittedCharactersImpl}</td>
</tr>
<tr valign="top">
<td>How is a date formatted and parsed?</td>
<td>{@link hirondelle.web4j.request.DateConverter}</td>
<td><tt>DateConverterImpl</tt></td>
<td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/DateConverterImpl.html">example</a></td>
</tr>
<tr valign="top">
<td>How is a <tt>Locale</tt> derived from the request?</td>
<td>{@link hirondelle.web4j.request.LocaleSource}</td>
<td><tt>LocaleSrc</tt></td>
<td>{@link hirondelle.web4j.request.LocaleSourceImpl}</td>
</tr>
<tr valign="top">
<td>How is the system clock defined?</td>
<td>{@link hirondelle.web4j.util.TimeSource}</td>
<td><tt>TimeSrc</tt></td>
<td>{@link hirondelle.web4j.util.TimeSourceImpl}</td>
</tr>
<tr valign="top">
<td>How is a <tt>TimeZone</tt> derived from the request?</td>
<td>{@link hirondelle.web4j.request.TimeZoneSource}</td>
<td><tt>TimeZoneSrc</tt></td>
<td>{@link hirondelle.web4j.request.TimeZoneSourceImpl}</td>
</tr>
<tr valign="top">
<td>What is the translation of this text, for a given <tt>Locale</tt>?</td>
<td>{@link hirondelle.web4j.ui.translate.Translator}</td>
<td><tt>TranslatorImpl</tt></td>
<td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/TranslatorImpl.html">example</a></td>
</tr>
<tr valign="top">
<td>How does the application obtain a database <tt>Connection</tt>?</td>
<td>{@link hirondelle.web4j.database.ConnectionSource}</td>
<td><tt>ConnectionSrc</tt></td>
<td><a href="http://www.javapractices.com/apps/fish/javadoc/src-html/hirondelle/web4j/config/ConnectionSrc.html">example</a></td>
</tr>
<tr valign="top">
<td>How is a <tt>ResultSet</tt> column translated into a given target type?</td>
<td>{@link hirondelle.web4j.database.ConvertColumn}</td>
<td><tt>ColToObject</tt></td>
<td>{@link hirondelle.web4j.database.ConvertColumnImpl}</td>
</tr>
<tr valign="top">
<td>How should an email be sent when a problem occurs?</td>
<td>{@link hirondelle.web4j.webmaster.Emailer}</td>
<td><tt>Email</tt></td>
<td>{@link hirondelle.web4j.webmaster.EmailerImpl}</td>
</tr>
<tr valign="top">
<td>How should the logging system be configured?</td>
<td>{@link hirondelle.web4j.webmaster.LoggingConfig}</td>
<td><tt>LogConfig</tt></td>
<td>{@link hirondelle.web4j.webmaster.LoggingConfigImpl}</td>
</tr>
<tr valign="top">
<td>Does this request/operation have a data ownership constraint?</td>
<td>{@link hirondelle.web4j.security.UntrustedProxyForUserId}</td>
<td><tt>OwnerFirewall</tt></td>
<td>{@link hirondelle.web4j.security.UntrustedProxyForUserIdImpl}</td>
</tr>
</table>
<p><span class="highlight">No conflict between the classes of different
applications will result, if application code is placed in the usual locations
under <tt>WEB-INF</span></tt>, and not in <i>shared</i> locations accessible
to multiple web applications. Since version 2.2 of the Servlet API, each
web application gets its own {@link java.lang.ClassLoader}, so no conflict
will result, as long as classes are placed in non-shared locations (which
is almost always the case).

<P>This class does not cache objects in any way.
*/
public final class BuildImpl {
 
  /*
   Note: some might make better use of generics here. BUT from the point of view of the
   caller, the forXXX methods should remain. That way the caller does not have to
   remember the interface class literal. (As well, the web.xml settings for
   intf/impl are text, not class literals.)
  */
 
  /**
   Called by the framework upon startup.
  
   <P>Extract all configuration which maps names of abstractions to names of corresponding 
   concrete implementations. Confirm both that all required interfaces
   have configured implementations, and that they can be loaded.
  
   <P>The implementation of {@link TimeSource} and {@link LoggingConfig} are treated slightly
   differently than the rest. Their implementations are found earlier than the others, since they
   are of immediate use.
  
   <P>See class comment for more information.
  */
  public static void init(ServletConfig aConfig) throws AppException {
    useWebXmlSettingsFirst(aConfig);
    doTimeSourceConfig();
    doLoggingConfig(aConfig);
    fLogger.config("________________________ STARTUP :Initializing WEB4J Controller. Reading in settings in web.xml._________");
    useStandardOrDefaultNameSecond();
    fLogger.config("Mapping of implementation classes : " + Util.logOnePerLine(fClassMapping));
  }

  /**
   Map a fully-qualified <tt>aAbstractionName</tt> into a concrete implementation.
 
   <P>This method should only be used for 'non-standard' items not covered by more 
   specific methods in this class. For example, when looking for the implementation of {@link LocaleSource},
   the {@link #forLocaleSource()} method should always be used instead of this method.
  
   <P>Implementation classes accessed by this method must have a <tt>public</tt> no-argument
   constructor. (This method is best suited for interfaces, and not abstract base classes.)
  
   <P>Uses {@link Class#newInstance}, with no arguments. If a problem occurs, a
   {@link RuntimeException} is thrown.
 
   @param aAbstractionName package-qualified name of an interface or abstract base class, as in
   "<tt>hirondelle.web4j.ApplicationInfo</tt>".
  */
  public static Object forAbstraction(String aAbstractionName){
    Class<?> implementationClass = fClassMapping.get(aAbstractionName);
    if ( implementationClass == null )  {
      throw new IllegalArgumentException(
        "No mapping to an implementation class found, for interface or abstract base class named " + Util.quote(aAbstractionName)
      );
    }
    Object result = null;
    try {
      result = implementationClass.newInstance();
    }
    catch (InstantiationException ex){
      handleCtorProblem(ex, implementationClass);
    }
    catch (IllegalAccessException ex) {
      handleCtorProblem(ex, implementationClass);
    }
    return result;
  }
 
  /**
   Map a fully-qualified <tt>aAbstractBaseClassName</tt> into a concrete implementation.
 
   <P>Intended for abstract base classes (ABC's) having a <tt>public</tt>
   constructor with known arguments. For example, this method is used by
   the {@link hirondelle.web4j.Controller} to build an implementation of
   {@link hirondelle.web4j.request.RequestParser}, by passing in a <tt>request</tt>
   and <tt>response</tt> object. (Implementations of that ABC are always expected to
   take those two particular constructor arguments.)
  
   <P>If a problem occurs, a {@link RuntimeException} is thrown.
  
   @param aAbstractBaseClassName package-qualified name of an Abstract Base Class, as in
   "<tt>hirondelle.web4j.ui.RequestParser</tt>".
   @param aCtorArguments <tt>List</tt> of arguments to be passed to the constructor of an 
   implementation class; the size of this list determines the selected constructor (by
   matching the number of parameters), and the iteration order of its items corresponds
   to the order of appearance of the formal constructor parameters.
  */
  public static Object forAbstractionPassCtorArgs(String aAbstractBaseClassName, List<Object> aCtorArguments){
    Object result = null;
    Class implClass = fClassMapping.get(aAbstractBaseClassName);
    Constructor ctor = ModelCtorUtil.getConstructor(implClass, aCtorArguments.size());
    try {
      result = ModelCtorUtil.buildModelObject(ctor, aCtorArguments);
    }
    catch (ModelCtorException ex){
      handleCtorProblem(ex, implClass);
    }
    return result;
  }
 
  /** Return the configured implementation of {@link ApplicationInfo}.  */
  public static ApplicationInfo forApplicationInfo(){
    return (ApplicationInfo)forAbstraction(APPLICATION_INFO.getAbstraction());
  }
 
  /** Return the configured implementation of {@link StartupTasks}.  */
  public static StartupTasks forStartupTasks(){
    return (StartupTasks)forAbstraction(STARTUP_TASKS.getAbstraction());
  }
 
  /** Return the configured implementation of {@link LoginTasks}.  */
  public static LoginTasks forLoginTasks(){
    return (LoginTasks)forAbstraction(LOGIN_TASKS.getAbstraction());
  }
 
  /** Return the configured implementation of {@link ConvertParamError}.  */
  public static ConvertParamError forConvertParamError(){
    return (ConvertParamError)forAbstraction(CONVERT_PARAM_ERROR.getAbstraction());
  }
 
  /** Return the configured implementation of {@link ConvertColumn}.  */
  public static ConvertColumn forConvertColumn(){
    return (ConvertColumn)forAbstraction(CONVERT_COLUMN.getAbstraction());
  }

  /** Return the configured implementation of {@link PermittedCharacters}.  */
  public static PermittedCharacters forPermittedCharacters(){
    return (PermittedCharacters)forAbstraction(PERMITTED_CHARACTERS.getAbstraction());
  }
 
  /** Return the configured implementation of {@link ConnectionSource}.  */
  public static ConnectionSource forConnectionSource(){
    return (ConnectionSource)forAbstraction(CONNECTION_SOURCE.getAbstraction());   
  }
 
  /** Return the configured implementation of {@link LocaleSource}.  */
  public static LocaleSource forLocaleSource(){
    return (LocaleSource)forAbstraction(LOCALE_SRC.getAbstraction());
  }

  /**
  Return the configured implementation of {@link TimeSource}.
 
  <P>When testing, an application may call this method in order to use a 'fake'
  system time.
 
   <P>Internally, WEB4J will always use this method when it needs the current time.
   This allows a fake system time to be shared between your application and WEB4J.
  */
  public static TimeSource forTimeSource(){
    return (TimeSource)forAbstraction(TIME_SRC.getAbstraction());
  }
 
  /** Return the configured implementation of {@link TimeZoneSource}.  */
  public static TimeZoneSource forTimeZoneSource(){
    return (TimeZoneSource)forAbstraction(TIME_ZONE_SRC.getAbstraction());
  }
 
  /** Return the configured implementation of {@link DateConverter}.  */
  public static DateConverter forDateConverter(){
    return (DateConverter)forAbstraction(DATE_CONVERTER.getAbstraction());
  }
 
  /** Return the configured implementation of {@link Translator}.  */
  public static Translator forTranslator(){
    return (Translator)forAbstraction(TRANSLATOR.getAbstraction());
  }
 
  /** Return the configured implementation of {@link ApplicationFirewall}.  */
  public static ApplicationFirewall forApplicationFirewall() {
    return (ApplicationFirewall)forAbstraction(APP_FIREWALL.getAbstraction());
  }
 
  /** Return the configured implementation of {@link SpamDetector}.  */
  public static SpamDetector forSpamDetector() {
    return (SpamDetector)forAbstraction(SPAM_DETECTOR.getAbstraction());
  }
 
  /** Return the configured implementation of {@link Emailer}.  */
  public static Emailer forEmailer() {
    return (Emailer)forAbstraction(EMAILER.getAbstraction());
  }
 
  /** Return the configured implementation of {@link ConvertParam}.  */
  public static ConvertParam forConvertParam() {
    return (ConvertParam)forAbstraction(CONVERT_PARAM.getAbstraction());
  }
 
  /** Return the configured implementation of {@link UntrustedProxyForUserId}.  */
  public static UntrustedProxyForUserId forOwnershipFirewall() {
    return (UntrustedProxyForUserId)forAbstraction(OWNER_FIREWALL.getAbstraction());
  }

  /**
    Add an implementation - intended for testing only.
     
     <P>This method allows testing code to configure a specific implementation class.
     Example: <PRE>BuildImpl.adHocImplementationAdd(TimeSource.class, MyTimeSource.class);</PRE>
     Calls to this method (often in a JUnit <tt>setUp()</tt> method) should be paired with a
     subsequent call to {@link #adHocImplementationRemove(Class)}.
  */
  public static void adHocImplementationAdd(Class aInterface, Class aImplementationClass){
     fClassMapping.put(aInterface.getName(), aImplementationClass);
  }
 
  /**
    Remove an implementation - intended for testing only.
    
    <P>This method allows testing code to configure a specific implementation class.
    Example: <PRE>BuildImpl.adHocImplementationRemove(TimeSource.class);</PRE>
    Calls to this method (often in a JUnit <tt>tearDown()</tt> method) should be paired with
    a previous call to {@link #adHocImplementationAdd(Class, Class)}.
  */
  public static void adHocImplementationRemove(Class aInterface){
    fClassMapping.remove(aInterface.getName());
  }
 
  // PRIVATE
 
  /**
   Key - interface name (String)
   Value - implementation class (Class)
  */
  private static final Map<String, Class<?>> fClassMapping = new LinkedHashMap<String, Class<?>>();
 
  private static final String IMPLEMENTATION_FOR = "ImplementationFor.";
  private static final Logger fLogger = Util.getLogger(BuildImpl.class);
 
  private BuildImpl(){
    //prevent construction by caller
  }
 
  /*
   Implementation Note.
   Early versions of this class did not work with class literals. Problem disappeared?
  */
 
  private static final String STANDARD_PACKAGE = "hirondelle.web4j.config.";
 
  //Items with no WEB4J default
  private static final StandardDefault APPLICATION_INFO = new StandardDefault(ApplicationInfo.class.getName(),"AppInfo");
  private static final StandardDefault STARTUP_TASKS = new StandardDefault(StartupTasks.class.getName(), "Startup");
  private static final StandardDefault LOGIN_TASKS = new StandardDefault(LoginTasks.class.getName(), "Login");
  private static final StandardDefault CONNECTION_SOURCE = new StandardDefault(ConnectionSource.class.getName(), "ConnectionSrc");
  private static final StandardDefault CONVERT_PARAM_ERROR = new StandardDefault(ConvertParamError.class.getName(), "ConvertParamErrorImpl");
  private static final StandardDefault TRANSLATOR = new StandardDefault(Translator.class.getName(), "TranslatorImpl");
  private static final StandardDefault DATE_CONVERTER = new StandardDefault(DateConverter.class.getName(), "DateConverterImpl");
 
  //Items with a WEB4J default
  private static final StandardDefault LOGGING_CONFIG = new StandardDefault(LoggingConfig.class.getName(), "LogConfig", LoggingConfigImpl.class.getName());
  private static final StandardDefault REQUEST_PARSER = new StandardDefault(RequestParser.class.getName(), "RequestToAction", RequestParserImpl.class.getName());
  private static final StandardDefault APP_FIREWALL = new StandardDefault(ApplicationFirewall.class.getName(), "AppFirewall", ApplicationFirewallImpl.class.getName());
  private static final StandardDefault CONVERT_COLUMN = new StandardDefault(ConvertColumn.class.getName(), "ConvertColumns", ConvertColumnImpl.class.getName());
  private static final StandardDefault LOCALE_SRC = new StandardDefault(LocaleSource.class.getName(), "LocaleSrc", LocaleSourceImpl.class.getName());
  private static final StandardDefault TIME_SRC = new StandardDefault(TimeSource.class.getName(), "TimeSrc", TimeSourceImpl.class.getName());
  private static final StandardDefault TIME_ZONE_SRC = new StandardDefault(TimeZoneSource.class.getName(), "TimeZoneSrc", TimeZoneSourceImpl.class.getName());
  private static final StandardDefault SPAM_DETECTOR = new StandardDefault(SpamDetector.class.getName(), "SpamDetect", SpamDetectorImpl.class.getName());
  private static final StandardDefault EMAILER = new StandardDefault(Emailer.class.getName(), "Email", EmailerImpl.class.getName());
  private static final StandardDefault CONVERT_PARAM = new StandardDefault(ConvertParam.class.getName(), "ConvertParams", ConvertParamImpl.class.getName());
  private static final StandardDefault PERMITTED_CHARACTERS = new StandardDefault(PermittedCharacters.class.getName(), "PermittedChars", PermittedCharactersImpl.class.getName());
  private static final StandardDefault OWNER_FIREWALL = new StandardDefault(UntrustedProxyForUserId.class.getName(), "OwnerFirewall", UntrustedProxyForUserIdImpl.class.getName());
 
  //OTHERS? MUST add below as well...

  private static void useWebXmlSettingsFirst(ServletConfig aConfig) throws AppException {
    Enumeration params = aConfig.getInitParameterNames();
    while ( params.hasMoreElements() ){
      String paramName = (String)params.nextElement();
      if ( paramName.startsWith(IMPLEMENTATION_FOR) ) {
        String interfaceName = paramName.substring(IMPLEMENTATION_FOR.length());
        String className = aConfig.getInitParameter(paramName);
        fClassMapping.put(interfaceName, buildWebXmlClass(className));
      }
    }
  }
 
  private static void handleCtorProblem(Exception ex, Class<?> aImplementationClass){
    String message = "Object construction by reflection failed for " + aImplementationClass.toString();
    fLogger.severe(message);
    throw new RuntimeException(message, ex);
  }
 
  /** The system time must be done first, since used everywhere, including the logging system. */
  private static void doTimeSourceConfig() throws AppException {
    addStandardDefaultIfNotInWebXml(TIME_SRC);
  }
 
  /** Extract and execute the LoggingConfig, earlier than all others.  */
  private static void doLoggingConfig(ServletConfig aConfig) throws AppException {
    addStandardDefaultIfNotInWebXml(LOGGING_CONFIG);
    executeLoggingConfig(aConfig);
  }
 
  private static void useStandardOrDefaultNameSecond() throws AppException {
    fLogger.config("For items *not* specified in web.xml, searching for implementations with 'standard' name.");
    fLogger.config("If no 'standard' implementation found, then will use the WEB4J 'default' implementation.");
    //does NOT include the items done 'early'
    addStandardDefaultIfNotInWebXml(APPLICATION_INFO);
    addStandardDefaultIfNotInWebXml(CONNECTION_SOURCE);
    addStandardDefaultIfNotInWebXml(CONVERT_PARAM_ERROR);
    addStandardDefaultIfNotInWebXml(TRANSLATOR);
    addStandardDefaultIfNotInWebXml(DATE_CONVERTER);
    addStandardDefaultIfNotInWebXml(STARTUP_TASKS);
    addStandardDefaultIfNotInWebXml(LOGIN_TASKS);
    addStandardDefaultIfNotInWebXml(REQUEST_PARSER);
    addStandardDefaultIfNotInWebXml(APP_FIREWALL);
    addStandardDefaultIfNotInWebXml(CONVERT_COLUMN);
    addStandardDefaultIfNotInWebXml(LOCALE_SRC);
    addStandardDefaultIfNotInWebXml(TIME_ZONE_SRC);
    addStandardDefaultIfNotInWebXml(SPAM_DETECTOR);
    addStandardDefaultIfNotInWebXml(EMAILER);
    addStandardDefaultIfNotInWebXml(CONVERT_PARAM);
    addStandardDefaultIfNotInWebXml(PERMITTED_CHARACTERS);
    addStandardDefaultIfNotInWebXml(OWNER_FIREWALL);
  }
 
  private static boolean isAlreadySpecified(String aInterfaceName){
    return fClassMapping.keySet().contains(aInterfaceName);
  }

  private static Class<?> buildWebXmlClass(String aClassName) throws AppException {
    Class<?> result = null;
    try {
      result = Class.forName(aClassName);
    }
    catch (ClassNotFoundException ex){
      throw new AppException(
        "Load of configured (or default) implementation class has failed. Class.forName() failed for " + Util.quote(aClassName), ex
      );
    }
    return result;
  }

  private static void addStandardDefaultIfNotInWebXml(StandardDefault aNames) throws AppException {
    if ( ! isAlreadySpecified(aNames.getAbstraction()) ){
      fClassMapping.put(aNames.getAbstraction(), buildStandardOrDefaultClass(aNames.getStandard(), aNames.getDefault()));
    }
  }
 
  private static Class<?> buildStandardOrDefaultClass(String aStandardName, String aDefaultName) throws AppException {
    Class<?> result = null;
    try {
      result = Class.forName(aStandardName);
    }
    catch (ClassNotFoundException ex){
      if( ! Util.textHasContent(aDefaultName) ){
        throw new AppException(
          "Load of configured implementation class has failed. Class.forName() failed for " + Util.quote(aStandardName), ex
        );
      }
      fLogger.config("Cannot see any class named " + Util.quote(aStandardName) + ". Will use default WEB4J implementation instead, named " + Util.quote(aDefaultName));
      try {
        result = Class.forName(aDefaultName);
      }
      catch (ClassNotFoundException exception){
        throw new AppException(
          "Load of default implementation has failed. Class.forName() failed for " + Util.quote(aDefaultName), exception
        );
      }
    }
    return result;
  }

  /** Execute the configured implementation of {@link LoggingConfig}.  */
  private static void executeLoggingConfig(ServletConfig aConfig) throws AppException {
    LoggingConfig loggingConfig = forLoggingConfig();
    loggingConfig.setup(aConfig);
  }
 
  /**  Return the configured implementation of {@link LoggingConfig}.  */
  private static LoggingConfig forLoggingConfig(){
    return (LoggingConfig)forAbstraction(LOGGING_CONFIG.getAbstraction());
  }
 
  /** Gathers the standard and default class names related to an abstraction.   */
  private static final class StandardDefault {
    StandardDefault(String aAbstraction, String aStandard){
      fAbstraction = aAbstraction;
      fStandard = STANDARD_PACKAGE + aStandard;
    }
    StandardDefault(String aAbstraction, String aStandard, String aDefault){
      fAbstraction = aAbstraction;
      fStandard = STANDARD_PACKAGE + aStandard;
      fDefault = aDefault;
    }
    boolean hasDefault(){
      return Util.textHasContent(fDefault);
    }
    String getAbstraction() {  return fAbstraction;    }
    String getDefault() {   return fDefault;   }
    String getStandard() {    return fStandard;   }
    private String fAbstraction;
    private String fStandard;
    private String fDefault;
  }
}
TOP

Related Classes of hirondelle.web4j.BuildImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.