You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by cr...@locus.apache.org on 2000/12/29 20:16:38 UTC

cvs commit: jakarta-struts/src/share/org/apache/struts/util GenericDataSource.java package.html

craigmcc    00/12/29 11:16:37

  Modified:    src/share/org/apache/struts/util GenericDataSource.java
                        package.html
  Log:
  Update developer documentation on the JDBC connection pool and message
  resources implementations.
  
  Revision  Changes    Path
  1.2       +81 -6     jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java
  
  Index: GenericDataSource.java
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- GenericDataSource.java	2000/11/26 05:11:31	1.1
  +++ GenericDataSource.java	2000/12/29 19:16:37	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java,v 1.1 2000/11/26 05:11:31 craigmcc Exp $
  - * $Revision: 1.1 $
  - * $Date: 2000/11/26 05:11:31 $
  + * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java,v 1.2 2000/12/29 19:16:37 craigmcc Exp $
  + * $Revision: 1.2 $
  + * $Date: 2000/12/29 19:16:37 $
    *
    * ====================================================================
    *
  @@ -73,15 +73,90 @@
   
   
   /**
  - * Generic data source implementation of the <code>DataSource</code>
  + * <p>Generic data source implementation of the <code>DataSource</code>
    * interface.  <b>WARNING</b> - This implementation does not know how to
    * provide connections with different username/password combinations.  It
    * always returns connections based on the username and password configured
  - * with <code>setUsername()</code> and <code>setPassword()</code>,
  - * respectively.
  + * with <code>setUser()</code> and <code>setPassword()</code>,
  + * respectively.</p>
    *
  + * <p>The following properties are supported by the standard
  + * <code>GenericDataSource</code> implementation:</p>
  + * <table>
  + * <tr>
  + *   <th width="15%">Property</th>
  + *   <th width="85%">Description</th>
  + * </tr>
  + * <tr>
  + *   <td align="center">autoCommit</td>
  + *   <td>Set to <code>true</code> if you want the connections returned to you
  + *       by calling <code>getConnection()</code> to be configured in
  + *       "auto-commit after every statement" mode.  The default value is
  + *       <code>true</code>, to conform to JDBC standard conventions.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">description</td>
  + *   <td>A short textual description of this data source.  This property is
  + *       required by the <code>javax.sql.DataSource</code> interface, but is
  + *       not used within this implementation.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">driverClass</td>
  + *   <td>The fully qualified class name of the JDBC driver to be utilized for
  + *       the connections created by this data source.  Consult the
  + *       documentation for your JDBC driver to identify the value to be
  + *       configured for this property.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">maxCount</td>
  + *   <td>The maximum number of JDBC connections that will be created by this
  + *       data source.  This value must be greater than or equal to the value
  + *       specified for the <code>minCount</count> property.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">minCount</td>
  + *   <td>The minimum number of JDBC connections to establish when this data
  + *       source is first opened.  This value must be less than or equal to the
  + *       value specified for the <code>maxCount</code> property.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">password</td>
  + *   <td>The database password used to establish the connections created by
  + *       this connection pool, in conjunction with the username specified in
  + *       the <code>user</code> property.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">readOnly</td>
  + *   <td>Set to <code>true</code> if you want the connections returned to you
  + *       by calling <code>getConnection()</code> to be configured for read only
  + *       operations.  This can result in more efficient database access,
  + *       because the database will know it does not need to retain undo logs
  + *       for rolling back the transaction.  The default value is
  + *       <code>false</code>.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">url</td>
  + *   <td>The connection URL to be passed to our JDBC driver when establishing
  + *       a new connection.  The value specified typically starts with
  + *       <code>jdbc:</code>, and includes a reference to the host (and,
  + *       optionally, the port number) at which the database server is listening
  + *       for connections, plus the name of the database to be opened.  Consult
  + *       the documentation for your JDBC driver to identify the value to be
  + *       configured for this property.</td>
  + * </tr>
  + * <tr>
  + *   <td align="center">user</td>
  + *   <td>The database username used to establish the connections created by
  + *       this connection pool, in conjunction with the password specified in
  + *       the <code>password</code> property.</td>
  + * </tr>
  + * </table>
  + *
  + * <p>In addition, you can add to the set of <code>Properties</code> passed to
  + * the JDBC driver by calling <code>addProperty()</code>.</p>
  + *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.1 $ $Date: 2000/11/26 05:11:31 $
  + * @version $Revision: 1.2 $ $Date: 2000/12/29 19:16:37 $
    */
   
   public class GenericDataSource implements DataSource {
  
  
  
  1.2       +477 -0    jakarta-struts/src/share/org/apache/struts/util/package.html
  
  Index: package.html
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/package.html,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- package.html	2000/12/16 19:01:44	1.1
  +++ package.html	2000/12/29 19:16:37	1.2
  @@ -14,6 +14,7 @@
   <a href="#doc.Messages">[Message Resources]</a>
   </div>
   
  +<hr>
   
   <a name="doc.Intro"></a>
   <h3>Introduction</h3>
  @@ -39,21 +40,497 @@
       particular user's preferred language.</li>
   </ul>
   
  +<hr>
   
   <a name="doc.Beans"></a>
   <h3>Beans and Properties</h3>
   
   <p>FIXME</p>
   
  +<hr>
   
   <a name="doc.JDBC"></a>
   <h3>JDBC Connection Pool</h3>
   
  +<h5>Background</h5>
   
  +<p>A large number of web applications require interaction with a relational
  +database to access or update persistently stored information.  In a typical
  +client-server application, each concurrent user opens their own database
  +connection at program initialization, and uses this connection throughout
  +the period of time the application is open.</p>
  +
  +<p>While this approach can work well in an environment where the number of
  +active users is reasonably fixed, it does not scale well to a web application
  +where the number of simultaneous users could be very large.  In addition, open
  +database connections (even when not actively used) do impose some overhead
  +costs, and most web application users (at a given instant) are reviewing the
  +contents of a previously generated page (or typing in their next set of input
  +information), rather than actively accessing the database.</p>
  +
  +<p>To deal with this situation, several basic strategies are possible:</p>
  +<ol>
  +<li>Open a connection on each request, do whatever processing is required,
  +    and then close the connection.</li>
  +<li>Open a connection for each user, and store it in the user's session.</li>
  +<li>Share a "pool" of open connections between all of the application's
  +    current users.</li>
  +</ol>
  +
  +<p>The first strategy has the virtue of simplicity - you merely need to open
  +a database connection any time you need one, perform the appropriate data
  +accesses and updates, and close the connection.  However, it suffers from a
  +major disadvantage:  on most databases, establishing a connection can be very
  +time consuming (often requiring multiple seconds of clock time), in order to
  +perform a database transaction that might take milliseconds.</p>
  +
  +<p>Opening a connection per user, as the second strategy suggests, is similar
  +to the approach taken with client-server applications described earlier.  As
  +long as the number of simultaneous users can be controlled at a manageable
  +number (such as with many intranet-based applications), this approach is
  +feasible.  However, it becomes unmanageable when the number of users can climb
  +rapidly to very large numbers (as is typical of many Internet-hosted public
  +applications), and still requires more overhead than a strategy that would
  +share a smaller number of connections.</p>
  +
  +<p>Connection pooling is an implementation of the third strategy.  It is based
  +on the assumption that most users of a web application will be interacting
  +locally with the last page that was sent to their browser.  The number of users
  +actually performing a request at any given time is usually a very small
  +percentage of the total number of active users, and during request processing
  +is the only time that a database connection is required.</p>
  +
  +<p>Struts provides a simple connection pool class called
  +<code>org.apache.struts.util.GenericDataSource</code>.  It allows you to
  +configure a set of connections (with identical connection parameters) to a
  +particular database, using a particular JDBC driver, and then share those
  +connections among a number of simultaneously operating threads (such as the
  +various request threads that are concurrently active in a servlet container).
  +The <code>GenericDataSource</code> class implements the
  +<code>javax.sql.DataSource</code> interface from the Java Database Connectivity
  +(version 2.0) Standard Extension API, so any programs you use to access it
  +should reference this interface, rather than the class name directly.
  +That way, you can migrate to a more advanced connection pool implementation
  +later, with little or no impact on your application.</p>
  +
  +<p>For more information about the JDBC 2.0 Standard Extension API, you can
  +download the spec (and the corresponding API classes), from
  +<a href="http://java.sun.com/products/jdbc">http://java.sun.com/products/jdbc</a>.  You can also find pointers to a substantial amount of other information
  +about available JDBC drivers, programming tutorials, and so on, at this
  +web address.</p>
  +
  +<h5>Initializing and Finalizing the Connection Pool</h5>
  +
  +<p>The following instructions show you how to configure the connection pool
  +class and use it, from any Java application.  As you will see below, the Struts
  +controller servlet offers you convenient mechanisms to configure one or more
  +connection pools, and make them available to Action classes and JSP pages by
  +storing the connection pool instances as servlet context attributes (in JSP
  +terms, application-scope beans).</p>
  +
  +<p>To configure a <code>GenericDataSource</code> instance, you must first
  +create one:</p>
  +<pre>
  +    GenericDataSource dataSource =
  +      new GenericDataSource();
  +</pre>
  +
  +<p>Next, you must set the appropriate properties, by calling the corresponding
  +JavaBeans property setter methods provided by this class.  (See the Javadoc
  +API for the <a href="GenericDataSource.html">GenericDataSource</a> class for
  +more details on the available properties).  An example of configuring the
  +connection pool object to a Postgres database might look like this:</p>
  +<pre>
  +    dataSource.setAutoCommit(false);
  +    dataSource.setDescription("My Database Connection Pool");
  +    dataSource.setDriverClass("org.postgresql.Driver");
  +    dataSource.setMaxCount(4);
  +    dataSource.setMinCount(1);
  +    dataSource.setPassword("mypassword");
  +    dataSource.setUrl("jdbc:postgresql://localhost/mydatabase");
  +    dataSource.setUser("myusername");
  +</pre>
  +
  +<p>Finally, you must <code>open()</code> the connection pool.  This will
  +establish the initial connections to the database (based on the value you have
  +configured for the <code>minCount</code> property).  As you use connections
  +from the pool in multiple threads, additional connections (up to the number
  +you specify with the <code>maxCount</code> property) will be created as needed.
  +</p>
  +<pre>
  +    try {
  +        dataSource.open();
  +    } catch (SQLException e) {
  +        ... deal with exception ...
  +    }
  +</pre>
  +
  +<p>When you are completely through with the connection pool, you can gracefully
  +close all of the currently open database connections by executing</p>
  +<pre>
  +    try {
  +        dataSource.close();
  +    } catch (SQLException e) {
  +        ... deal with exception ...
  +    }
  +</pre>
  +
  +<h5>Using the Generic Connection Pool</h5>
  +
  +<p>To access the database from within an application class, you must follow a
  +simple four-step procedure each time you need a connection:</p>
  +<ol>
  +<li>Acquire a connection from the connection pool.</li>
  +<li>Perform the database operations required by your application.</li>
  +<li>Cause the last database transaction to be committed or rolled back
  +    (commits are required on many databases to ensure that the database
  +    operations you just performed are permanently stored or not).</li>
  +<li>"Close" the connection, which returns it to the connection pool for
  +    reuse later.</li>
  +</ol>
  +
  +<p>An example code sequence that performs this procedure might look like
  +this:</p>
  +<pre>
  +    DataSource dataSource = ... acquire reference to dataSource ...
  +    Connection conn = null;
  +    PreparedStatement stmt = null;
  +    ResultSet rs = null;
  +    try {
  +        conn = dataSource.getConnection();
  +        stmt = conn.prepareStatement("SELECT cust_id, name FROM customers" +
  +          " WHERE (last_purchase_date >= ?)" +
  +          " ORDER BY name");
  +        stmt.setDate(1, lastPurchaseDate);
  +        rs = stmt.executeQuery();
  +        while ((row = rs.next()) != null) {
  +            ... process this row ...
  +        }
  +        rs.close();
  +        rs = null;
  +        stmt.close();
  +        stmt = null;
  +        conn.commit();
  +        conn.close();
  +        conn = null;
  +    } catch (SQLException e) {
  +        if (rs != null) {
  +            try {
  +                rs.close();
  +            } catch (SQLException f) {
  +                ;
  +            }
  +            rs = null;
  +        }
  +        if (stmt != null) {
  +            try {
  +                stmt.close();
  +            } catch (SQLException f) {
  +                ;
  +            }
  +            stmt = null;
  +        }
  +        if (conn != null) {
  +            try {
  +                conn.rollback();
  +            } catch (SQLException f) {
  +                ... deal with exception ...
  +            }
  +        }
  +        ... deal with exception ...
  +    } finally {
  +        if (conn != null) {
  +            try {
  +                conn.close();
  +            } catch (SQLException f) {
  +                ... deal with exception ...
  +            }
  +            conn = null;
  +        }
  +    }
  +</pre>
  +
  +<p>One aspect of the above code example that might surprise developers who
  +have previously used JDBC connections individually is the idea of calling
  +close() on the Connection.  Normally, this call will sever the Connection's
  +underlying link to the database, and render that Connection unuseable for
  +any further operations.  However, when used in a connection pool environment,
  +the actual Connection you receive by calling <code>getConnection()</code>
  +is a customized "wrapper" around a real JDBC Connection instance.  Calling
  +<code>close()</code> on this wrapper simply causes this connection to be
  +returned to the pool.</p>
  +
  +<p>What would happen if your application failed to return a connection to the
  +pool when it was through?  As you might expect, that particular connection
  +becomes "lost" to the server, and can never again be used (even though it
  +remains connected to the database throughout the life of the connection pool
  +itself).  If this happens repeatedly, you will eventually exhaust the pool
  +of available connections, and application processing will stop.</p>
  +
  +<p>To avoid this problem, your application logic must ensure that it
  +<strong>ALWAYS</strong> returns allocated connections to the pool, no matter
  +what problems might happen in the interim.  The Java language provides one
  +convenient mechanism to achieve this - using a <code>finally</code> block,
  +as in the code example above.  This is not the only way to ensure that a
  +connection is always returned, but it is very convenient.</p>
  +
  +<h5>Using Connection Pools with the Struts Controller Servlet</h5>
  +
  +<p>If your application is running underneath the Struts controller servlet
  +(<code>org.apache.struts.action.ActionServlet</code>), you can take advantage
  +of the servlet's ability to preconfigure one or more connection pools for you,
  +based on information included in the <code>struts-config.xml</code> file.
  +Simply include a section that looks like this:</p>
  +<pre>
  +    &lt;data-sources&gt;
  +        &lt;data-source
  +          autoCommit="false" description="My Database Connection Pool"
  +          driverClass="org.postgresql.Driver" maxCount="4" minCount="1"
  +          password="mypassword" url="jdbc:postgresql://localhost/mydatabase"
  +          user="myusername"
  +        /&gt;
  +    &lt;/data-sources&gt;
  +</pre>
  +
  +<p>After being initialized, the connection pools will be stored as servlet
  +context attributes under the bean name specified by the <code>key</code>
  +attribute.  If you did not specify a <code>key</code>, the default key is the
  +value of the string constant <code>Action.DATA_SOURCE_KEY</code>.  Thus, you
  +can access and utilize a connection, from within an <code>Action</code> class,
  +like this (for the default data source):</p>
  +<pre>
  +    DataSource dataSource = (DataSource)
  +      servlet.getServletContext().getAttribute(Action.DATA_SOURCE_KEY);
  +    conn = dataSource.getConnection();
  +    ... perform required functions as in the previous example ...
  +    conn.close();
  +</pre>
   
  +<hr>
  +
   <a name="doc.Messages"></a>
   <h3>Message Resources</h3>
   
  +<h5>Background</h5>
  +
  +<p>Modern applications often include the requirement to support multiple
  +languages, for users who prefer to interact in a language other than the
  +default language configured on the server platform.  In addition, sentences
  +often need to be constructed, with dynamic content whose placement in the
  +message depends on the standard sentence structure in that particular
  +language.</p>
  +
  +<p>The standard Java platform includes a family of classes
  +(<code>java.util.ResourceBundle</code>) designed to support looking up message
  +strings based on a standard "key".  The resource bundle classes automatically
  +access a Java class (or properties file) that is named with a naming
  +convention that includes the Locale to which messages in that class (or file)
  +pertain.  However, this selection is based only on the default Locale of the
  +server platform, and cannot be adjusted on a per-user basis as required for an
  +internationalized web application.</p>
  +
  +<p>Struts includes a family of classes
  +(<code>org.apache.struts.util.MessageResources</code>) that extends the basic
  +approach to looking up message strings by key, allowing you to optionally
  +specify a Locale along with the key.  In this way, you can build applications
  +that let your users select which Locale they wish to operate within, and then
  +look up messages in that language - using the same message keys no matter what
  +language is selected.</p>
  +
  +<p>In addition to supporting dynamic selection of a Locale for message lookup,
  +the <code>MessageResources</code> family of classes optionally allow you to
  +specify up to four parameter replacement objects, which are used to replace the
  +parameter placeholders "{0}" through "{3}" in the retrieved message.  This
  +replacement uses the facilities of the standard Java
  +<code>java.text.MessageFormat</code> class, which supports many extended
  +formatting capabilities as well.</p>
  +
  +<p>For more information about internationalized messages, consult the following
  +resources in your Java Development Kit documentation bundle:</p>
  +<ul>
  +<li><em>Internationalization Info</em> - General information on Java's standard
  +    support for internationalized applications can be found at
  +    <code>&lt;$JAVA_HOME/docs/guide/internat/index.html&gt;</code>.
  +    The "Internationalization Overview" section includes useful information
  +    about Locales, localized resources, message formatting, and other
  +    relevant topics.</li>
  +<li><em>Internationalization Tutorial</em> - The Java Language Tutorial has a
  +    comprehensive trail covering internationalization, available at:
  +    <a href="http://java.sun.com/docs/books/tutorial/i18n/index.html">
  +    http://java.sun.com/docs/books/tutorial/i18n/index.html</a>.</li>
  +<li><em>Javadoc APIs</em> - You will want to consult the Javadoc API
  +    documentation for the following standard Java classes:
  +    <ul>
  +    <li><code>java.text.MessageFormat</code>
  +    <li><code>java.util.ResourceBundle</code>
  +    <li><code>java.util.PropertyResourceBundle</code>
  +    <li><code>java.util.Properties</code> - See the documentation for the
  +        <code>load()</code> method for the valid syntax of properties files
  +        that you prepare.</li>
  +    </ul></li>
  +</ul>
  +
  +<h5>Using the Standard MessageResources Implementation</h5>
  +
  +<p>The standard <code>MessageResources</code> implementation provided by the
  +Struts library uses Java properties files to initialize message strings, in a
  +manner very similar to that supported by the
  +<code>java.util.PropertyResourceBundle</code> class.  The following steps are
  +required to use these facilities in your Java application.</p>
  +
  +<p>First, prepare a Java properties file for each language (or Locale) in which
  +you wish to support your messages.  The filenames you use must conform to the
  +naming convention for property resource bundles, as described in the
  +documentation referenced above.  Be sure you use the same message keys in each
  +file to identify the same message.</p>
  +
  +<p>For example, you might prepare files in French, Spanish, and English that
  +contain language-specific versions of the word "Hello".  The French file would
  +be named <code>Messages_fr.properties</code> and contain the following:</p>
  +<pre>
  +    hi=Bonjour
  +</pre>
  +<p>while the Spanish and English files would be named
  +<code>Messages_es.properties</code> and <code>Messages_en.properties</code>
  +respectively.  The corresponding message string definitions would say
  +<code>hi=Hola</code> and <code>hi=Hello</code> in these files.</p>
  +
  +<p>Second, place these properties files into the class path for your
  +application, exactly as you would with class files themselves.  The name
  +actually used to load resources will look like a fully qualified Java class
  +name (with appropriate package prefixes), so the file should be nested inside
  +a directory structure that matches the packaging (either in an unpacked
  +directory, or within a JAR file, as appropriate).  For example, assume you
  +place directory "foo" on your classpath, and stored the above properties files
  +in directory "foo/com/mycompany/mypackage".  (If you were using a JAR file like
  +"foo.jar" instead, the files would be in directory "com/mycompany/mypackage"
  +within the JAR file).
  +
  +<p>Third, initialize a <code>MessageResources</code> object that corresponds
  +to the set of properties files for a particular name, within a particular
  +package.  The easiest way to do this is to initialize a variable in your main
  +application class, like this:</p>
  +<pre>
  +    public static MessageResources messages =
  +     MessageResources.getMessageResources("com.mycompany.mypackage.Messages");
  +</pre>
  +
  +<p>Note that the "com.mycompany.mypackage" part of the name matches the package
  +directory into which you placed your properties files, and "Messages" is the
  +filename prefix for the particular family of properties files supported by this
  +<code>MessageResources</code> instance.  Depending on your development process,
  +you might find it convenient to store all message strings for an entire
  +application in a single properties file family, or to have several families -
  +in Struts, for example, there is a family of properties files for each Java
  +package.</p>
  +
  +<p>To access a message string with a particular Locale, execute a statement
  +like this:</p>
  +<pre>
  +    Locale locale = ... select the locale to be used ...
  +    String message = messages.getMessage(locale, "hi");
  +</pre>
  +
  +<p>In this case, the variable <code>message</code> will contain the message
  +string corresponding to the key "hi", in the language that corresponds to the
  +locale that was selected.</p>
  +
  +<p>For an example of message formatting with replaceable parameters, assume
  +that the message strings looked like this, instead (only the English version
  +is shown - corresponding changes would be made in the other files):</p>
  +<pre>
  +    hi=Hello {0}
  +</pre>
  +
  +<p>Now, you can personalize the retrieved message like this:</p>
  +<pre>
  +    Locale locale = ... select the locale to be used ...
  +    String name = "Joe";
  +    String message = messages.getMessage(locale, "hi", name);
  +</pre>
  +
  +<p>and the marker "{0}" will have been replaced by the specified name (Joe),
  +no matter which language is in use.  See the JavaDoc API documentation for the
  +<code>java.text.MessageFormat</code> class for more advanced uses of the
  +parameter replacement mechanism.</p>
  +
  +<h5>Developing Your Own MessageResources Implementation</h5>
  +
  +<p>In the above example, we were using the default
  +<code>MessageResources</code> implementation supplied by Struts, which uses
  +property files to store the message strings.  It is also possible to create
  +customized mechanisms to retrieve messages (such as loading them on demand
  +from a database).  The steps required are as follows:</p>
  +<ul>
  +<li>Create a customized subclass of
  +    <code>org.apache.struts.util.MessageResources</code> that implements
  +    message lookup operations as you require.</li>
  +<li>Create a customized subclass of
  +    <code>org.apache.struts.util.MessageResourcesFactory</code> that will
  +    create an instance of your custom <code>MessageResources</code> class
  +    when the <code>createResources</code> method is called.  Note that the
  +    "config" argument to this method can be used to select families of
  +    messages in any manner appropriate to your needs - you are not required
  +    to emulate the "fully qualified Java class name" approach that is used
  +    by the standard <code>PropertyMessageResourcesFactory</code> class.</li>
  +<li>Tell the <code>MessageResourcesFactory</code> class the name of the
  +    customized <code>MessageResourcesFactory</code> implementation to use
  +    when creating new factory instances.</li>
  +<li>Create a new factory instance.</li>
  +<li>Ask the new factory instance to create a <code>MessageResources</code>
  +    instance for you.</li>
  +</ul>
  +
  +<p>A code example that illustrates this technique is:</p>
  +<pre>
  +    MessageResourcesFactory.setFactoryClass("com.mycompany.mypkg.MyFactory");
  +    MessageResourcesFactory factory = MessageResourcesFactory.createFactory();
  +    MessageResources resources =
  +     factory.createResources("configuration information");
  +</pre>
  +
  +<p>Once you have created your custom MessageResources instance, you utilize it
  +to access message strings (with or without parameter replacement objects),
  +exactly as we illustrated with the standard implementation in the previous
  +section.</p>
  +
  +<h5>Using MessageResources With Struts</h5>
  +
  +<p>If your application uses the Struts controller servlet, you can optionally
  +configure Struts to load an application-specific message resources instance for
  +you, and make it available as a servlet context attribute (in JSP terms, an
  +application-scope bean).  This mechanism is managed by setting the following
  +servlet initialization parameters in the web application deployment descriptor:
  +</p>
  +<ul>
  +<li><strong>application</strong> - The configuration string that will be
  +    passed to the <code>createResources()</code> method of the message
  +    resources factory, in order to identify the family of resources to be
  +    supported.  If you use the standard message resources factory, this must
  +    be the base fully qualified name of the property resources files used
  +    to contain these messages, as illustrated above.</li>
  +<li><strong>factory</strong> - Fully qualified Java class name of the
  +    <code>MessageResourcesFactory</code> to be used.  By default, the standard
  +    implementation provided by Struts
  +    (<code>org.apache.struts.util.PropertyMessageResourcesFactory</code>)
  +    will be used.</li>
  +</ul>
  +
  +<p>Struts provides several JSP custom tags that assume the existence of a
  +<code>java.util.Locale</code> attribute in the user's session, under the key
  +named by the constant string value of <code>Action.LOCALE_KEY</code>.  Your own
  +application logic can set this attribute at any time, or you can ask Struts to
  +set it automatically (if not already set) based on the
  +<code>Accept-Language</code> HTTP header included with the request.  There are
  +two mechanisms by which you request Struts to perform this service:</p>
  +<ul>
  +<li>To have this service performed on every request submitted to the controller
  +    servlet, set the servlet initialization parameter <code>locale</code> to
  +    the value <code>true</code> in the application deployment descriptor.</li>
  +<li>To have this service performed by a JSP page when it is accessed directly
  +    by a user, utilize a <code>&lt;form:html ... locale="true" ... /&gt;</code>
  +    tag at the top of each page.</li>
  +</ul>
   
   </body>
   </html>