<document>
<header>
<product>resin</product>
<type>tutorial</type>
<title>Application configuration files using a WebBeans object</title>

<description>
<p>Applications often need to read, and possibly write,
configuration files.   An excellent way to
accomplish this is to implement a custom singleton, which is
easily configured and easily obtained from anywhere in the
application.</p>

<p>This implementation of the concept allows you to configure a base
directory for configuration files.  An object of type
<code>AppConfig</code> is obtained with a <code>javax.webbeans.In</code>
injection. It is used to open
files relative to the base directory.</p>
</description>
 <tutorial-startpage>index.jsp</tutorial-startpage>

</header>
<body>


<summary/>

<s1 title="Files in this tutorial">
<deftable>
<tr>
  <th>File</th>
  <th>Description</th>
</tr>
<tr>
  <td><viewfile-link file="WEB-INF/web.xml"/></td>
  <td>Configure the AppConfig object as a resource</td>
</tr>
<tr>
  <td><viewfile-link file="WEB-INF/classes/example/AppConfig.java"/></td>
  <td>The AppConfig object provides input and output streams to configuration files</td>
</tr>
<tr>
  <td><viewfile-link file="WEB-INF/classes/example/TestServlet.java"/></td>
  <td>A simple example usage of AppConfig that reads and writes a file</td>
</tr>
<tr>
  <td><viewfile-link file="index.jsp"/></td>
  <td>The starting page for the tutorial</td>
</tr>
</deftable>
</s1>

<s1 title="The java code for a custom Singleton">
<p>A custom singleton is a standard java-bean (see <a href="doc|resin-ioc.xtp">Resin-IoC</a>).  Setter methods like <code>setFoo(String foo)</code> are
used to set values
that are specified in the configuration.</p>

<p>In this case, a single setter is provided that matches the
configuration parameter "config-files-location".  The
<code>@PostConstruct</code> annotation tells Resin to call the
<code>init()</code> method after all of the setters
have been called.</p>

<example title="AppConfig.java">
import javax.annotations.PostConstruct;

public class AppConfig {
  ConfigFilesLocation _cfl = null;

  /**
   * Set the base for subsequent call's to openConfigFileRead()
   * and openConfigFileWrite()
   *
   * @param location a file path or url
   */
  public void setConfigFilesLocation(String location)
    throws Exception
  {
    _cfl = new ConfigFilesLocation();
    _cfl.setLocation(location);
  }

  @PostConstruct
  public void init()
    throws Exception
  {
    if (_cfl == null)
      throw new Exception("'config-files-location' must be set");
  }

  ...
</example>
</s1>

<s1 title="Configuring the custom singleton">

<p>Configuration of the singleton is done with the
<a config-tag="bean"/> tag.</p>

<p>The example here configures the location of the configuration files
as <code>WEB-INF/config</code> (which means you need to make
sure the directory exists for the example to work).  It is good to
hide the files somewhere under <code>WEB-INF</code>, because a browser
will not be able to read the files, just the application.</p>

<p>The <a href="doc|el-var.xtp">EL configuration</a> variable <var>webApp.root</var> is used.</p>

<example title="Configuring the AppConfig singleton in resin-web.xml">
&lt;web-app xmlns="http://caucho.com/ns/resin">

  &lt;bean class="example.AppConfig">
    &lt;init&gt;
      &lt;config-files-location&gt;${webApp.root}/WEB-INF/config&lt;/config-files-location&gt;
    &lt;/init&gt;
  &lt;/bean&gt;

&lt;/web-app>
</example>

</s1>

<s1 title="Obtaining and using the object">

<p>An instance of the object is retrieved in the application using
dependency injection. In this example servlet, we'll use field-based
injection, marked by the <code>@javax.webbeans.In</code> annotation.
We could also use method injection.</p>

<p>Resin will look in the WebBeans registry for the <code>AppConfig</code>
object that we've configured in the resin.conf, and inject it into the
servlet.  Resin will report any errors in looking up
the <code>AppConfig</code> object, e.g. if it's not configured in the
resin.conf or if you've configured multiple <code>AppConfig</code>
instances.</p>

<example title="Obtaining the AppConfig object">
import javax.webbeans.In;

public class TestServlet extends GenericServlet {
  @In AppConfig _appConfig;
}
</example>

<p><code>_appConfig</code> is used to open the
configuration files for reading and writing.</p>

<example title="Using the AppConfig object">
...

    InputStream is = _appConfig.openConfigFileRead(inputFile);

...

    OutputStream os = _appConfig.openConfigFileWrite(outputFile);

...
</example>

</s1>

<s1 title="Variation - Hiding the configuration file with getters">

<p>The example in this tutorial is easily modified to allow the hiding of the
configuration file behind <code>get</code> methods of the bean.  Implementing
getters on the configuration bean abstracts the configuration information,
protecting code which uses the configuration information from implementation
details of how the configuration information is read and stored.
</p>

<example title="Hiding the configuration file with getters">
package example;

import java.util.*;
import java.io.*;
import javax.webbeans.*;

@Component
public class AppConfig {
  private final static String DEFAULT_PROPERTIES = "example/AppConfig.properties";

  private String _configFile;
  private Properties _properties;


  /**
   * Optionally set the name of a file that provides properties that override
   * the defaults.  The defaults are obtained from a file in the classpath 
   * named 'example/AppConfig.properties'
   *
   * For example, the file containing default properties might be in 
   * WEB-INF/classes/example/AppConfig.properties,
   * or if AppConfig.class is in a jar, the AppConfig.properties 
   * could be in the jar file alongside the AppConfig.class file.
   *
   * AppConfig.properties contains values placed there by the developer.
   * The &lt;config-file&gt; is used to indicate a file that specifies properties
   * that override the defaults, perhaps properties that change depending 
   * on the deployment environment.
   */
  public void setConfigFile(String configFile)
    throws Exception
  {
    _configFile = configFile;
  }

  @PostConstruct
  public void init()
    throws Exception
  {
    InputStream is = null;

    if (_configFile != null) {
      // the properties in _configFile override the defaults
      is = new FileInputStream(_configFile);

      _properties = new Properties(defaults);
      _properties.load(is);
    }
    else {
      // try to find a default configuration file in the classpath
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      is = loader.getResourceAsStream(DEFAULT_PROPERTIES);

      if (is != null)
        _properties = new Properties();
        _properties.load(is);
      }
      else {
        // throw an exception here to make the defaults required
        throw new FileNotFoundException(DEFAULT_PROPERTIES);
      }
    } 
  }

  public String getFoo()
  { 
    return _properties.getProperty("foo");
  }

  public String getBar()
  { 
    return _properties.getProperty("bar");
  }
}
</example>
<example>
&lt;web-app xmlns="http://caucho.com/ns/resin"&gt;
 &lt;bean class="example.AppConfig"/>
 &lt;/resource&gt;
&lt;/web-app&gt;

or

&lt;web-app xmlns="http://caucho.com/ns/resin"&gt;
 &lt;bean class="example.AppConfig">
    &lt;init&gt;
      &lt;config-file&gt;${webApp.root}/WEB-INF/AppConfig-override.properties&lt;/config-file&gt;
    &lt;/init&gt;
 &lt;/bean&gt;

&lt;/web-app&gt;
</example>
<example>
package example;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.webbeans.In;

import java.io.*;
import java.util.*;

public class TestServlet extends HttpServlet {
  @In AppConfig _appConfig;

  ...

  String foo = _appConfig.getFoo();
  String bar = _appConfig.getBar();

  ...

}
</example>

</s1>

<s1 title="Availability of AppConfig from different web-apps">

<p>
The availability of AppConfig to different web-apps depends upon the context
that the &lt;bean ...&gt; configuration is placed within.
</p>

<p>
If the configuration is placed as a child of &lt;web-app&gt;, then that
instance of AppConfig is available only to that web-app.
</p>

<example title="WEB-INF/resin-web.xml local to web-app">
&lt;web-app xmlns="http://caucho.com/ns/resin"&gt;

 &lt;bean class="example.AppConfig"/>

&lt;/web-app&gt;
</example>

<p>
If it is placed as a child of &lt;host&gt;, that instance of AppConfig is
available to all web-apps within the host.
</p>

<example title="shared host configuration in resin.conf">
&lt;resin xmlns="http://caucho.com/ns/resin">
&lt;cluster id="">

  &lt;host&gt;

    &lt;bean class="example.AppConfig"/>

    ...

  &lt;/host&gt;

&lt;/cluster>
&lt;/resin>
</example>

<p>
If the &lt;bean> is placed as a child of &lt;cluster&gt;, that instance of
AppConfig is available to all web-apps within all hosts within that cluster.
</p>

<example title="shared cluster configuration in resin.conf">
&lt;resin xmlns="http://caucho.com/ns/resin">
&lt;cluster id="">

   &lt;bean class="example.AppConfig">

   &lt;host id="">
      ...
   &lt;/host id="">
  ...
&lt;/cluster&gt;
&lt;/resin&gt;
</example>

<p>
In the case of &lt;cluster&gt; or &lt;host&gt;, the example.AppConfig class
needs to be available in the classpath.  The easiest way to accomplish that is
to place a jar with that class in $RESIN_HOME/lib, or you can use an explicit 
<a config-tag="class-loader"/>.
</p>

</s1>

</body>
</document>
