You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by Nacho <na...@siapi.es> on 2000/12/15 06:12:47 UTC

RE: cvs commit: jakarta-struts/src/share/org/apache/struts/util M essageResourcesFactory.java PropertyMessageResources.java PropertyMessage ResourcesFactory.java ServletContextWriter.java MessageResources.java

Thanks!! 

Is good to see the King.... eres el mejor :-) que no el mas rapido :-(

I understand why you didnt commit my poor code :-)

Saludos ,
Ignacio J. Ortega


> -----Mensaje original-----
> De: craigmcc@locus.apache.org [mailto:craigmcc@locus.apache.org]
> Enviado el: viernes 15 de diciembre de 2000 4:08
> Para: jakarta-struts-cvs@apache.org
> Asunto: cvs commit: jakarta-struts/src/share/org/apache/struts/util
> MessageResourcesFactory.java PropertyMessageResources.java
> PropertyMessageResourcesFactory.java ServletContextWriter.java
> MessageResources.java
> 
> 
> craigmcc    00/12/14 19:08:12
> 
>   Modified:    src/share/org/apache/struts/action ActionServlet.java
>                src/share/org/apache/struts/util MessageResources.java
>   Added:       src/share/org/apache/struts/util
>                         MessageResourcesFactory.java
>                         PropertyMessageResources.java
>                         PropertyMessageResourcesFactory.java
>                         ServletContextWriter.java
>   Log:
>   Replace the previous implementation of MessageResources 
> with a set of
>   classes offering the following features:
>   
>   - The MessageResources instance is now Serializable, to make it work
>     in application servers that require this even for 
> application scope
>     beans
>   
>   - The MessageResources implementation class (and the 
> implementation class
>     for the corresponding MessageResourcesFactory) are 
> pluggable at run time
>     so that you can provide your own MessageResources implementations.
>   
>   - The Struts controller servlet accepts a new "factory" 
> initialization
>     parameter to set the name of the MessageResourcesFactory 
> class to use.
>   
>   - The default implementation reads resource property files from the
>     web app class path, just as the previous version did.
>   
>   - No changes in existing classes are required if you always want the
>     currently registered default implementation class.
>   
>   Revision  Changes    Path
>   1.42      +29 -6     
> jakarta-struts/src/share/org/apache/struts/action/ActionServlet.java
>   
>   Index: ActionServlet.java
>   ===================================================================
>   RCS file: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/action/Ac
> tionServlet.java,v
>   retrieving revision 1.41
>   retrieving revision 1.42
>   diff -u -r1.41 -r1.42
>   --- ActionServlet.java	2000/12/06 19:15:58	1.41
>   +++ ActionServlet.java	2000/12/15 03:08:09	1.42
>   @@ -1,7 +1,7 @@
>    /*
>   - * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/action/Ac
> tionServlet.java,v 1.41 2000/12/06 19:15:58 craigmcc Exp $
>   - * $Revision: 1.41 $
>   - * $Date: 2000/12/06 19:15:58 $
>   + * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/action/Ac
> tionServlet.java,v 1.42 2000/12/15 03:08:09 craigmcc Exp $
>   + * $Revision: 1.42 $
>   + * $Date: 2000/12/15 03:08:09 $
>     *
>     * 
> ====================================================================
>     *
>   @@ -85,6 +85,8 @@
>    import org.apache.struts.util.BeanUtils;
>    import org.apache.struts.util.GenericDataSource;
>    import org.apache.struts.util.MessageResources;
>   +import org.apache.struts.util.MessageResourcesFactory;
>   +import org.apache.struts.util.ServletContextWriter;
>    import org.xml.sax.SAXException;
>    
>    
>   @@ -161,6 +163,9 @@
>     * <li><strong>detail</strong> - The debugging detail 
> level for the Digester
>     *     we utilize in <code>initMapping()</code>, which 
> logs to System.out
>     *     instead of the servlet log.  [0]</li>
>   + * <li><strong>factory</strong> - The Java class name of the
>   + *     <code>MessageResourcesFactory</code> used to create 
> the application
>   + *     <code>MessageResources</code> object.</li>
>     * <li><strong>forward</strong> - The Java class name of 
> the ActionForward
>     *     implementation to use 
> [org.apache.struts.action.ActionForward].
>     *     Two convenient classes you may wish to use are:
>   @@ -203,7 +208,7 @@
>     * </ul>
>     *
>     * @author Craig R. McClanahan
>   - * @version $Revision: 1.41 $ $Date: 2000/12/06 19:15:58 $
>   + * @version $Revision: 1.42 $ $Date: 2000/12/15 03:08:09 $
>     */
>    
>    public class ActionServlet
>   @@ -259,6 +264,13 @@
>    
>    
>        /**
>   +     * The Java class name of the 
> <code>MessageResourcesFactory</code>
>   +     * class for the application message resources bundle.
>   +     */
>   +    protected String factoryClass = null;
>   +
>   +
>   +    /**
>         * The Java class name of the ActionFormBean 
> implementation class to use.
>         */
>        protected String formBeanClass =
>   @@ -887,10 +899,21 @@
>    	String value = 
> getServletConfig().getInitParameter("application");
>    	if (value == null)
>    	    return;
>   +        String factory =
>   +            getServletConfig().getInitParameter("factory");
>    	if (debug >= 1)
>    	    log(internal.getMessage("applicationLoading", value));
>    	try {
>   -	    application = MessageResources.getMessageResources(value);
>   +            MessageResourcesFactory.setDefaultWriter
>   +                (new ServletContextWriter(getServletContext()));
>   +            String oldFactory =
>   +                MessageResourcesFactory.getFactoryClass();      
>   +            if (factory != null)
>   +                MessageResourcesFactory.setFactoryClass(factory);
>   +            MessageResourcesFactory factoryObject =
>   +                MessageResourcesFactory.createFactory();
>   +            application = factoryObject.createResources(value);
>   +            MessageResourcesFactory.setFactoryClass(oldFactory);
>    	    value = getServletConfig().getInitParameter("null");
>    	    if (value == null)
>    		value = "true";
>   @@ -899,7 +922,7 @@
>    		application.setReturnNull(true);
>    	    else
>    		application.setReturnNull(false);
>   -	} catch (MissingResourceException e) {
>   +	} catch (Throwable e) {
>    	    log(internal.getMessage("applicationResources", value), e);
>    	    throw new UnavailableException
>    		(internal.getMessage("applicationResources", value));
>   
>   
>   
>   1.5       +164 -213  
> jakarta-struts/src/share/org/apache/struts/util/MessageResources.java
>   
>   Index: MessageResources.java
>   ===================================================================
>   RCS file: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/util/Mess
> ageResources.java,v
>   retrieving revision 1.4
>   retrieving revision 1.5
>   diff -u -r1.4 -r1.5
>   --- MessageResources.java	2000/08/01 20:04:02	1.4
>   +++ MessageResources.java	2000/12/15 03:08:10	1.5
>   @@ -1,7 +1,7 @@
>    /*
>   - * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/util/Mess
> ageResources.java,v 1.4 2000/08/01 20:04:02 craigmcc Exp $
>   - * $Revision: 1.4 $
>   - * $Date: 2000/08/01 20:04:02 $
>   + * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/util/Mess
> ageResources.java,v 1.5 2000/12/15 03:08:10 craigmcc Exp $
>   + * $Revision: 1.5 $
>   + * $Date: 2000/12/15 03:08:10 $
>     *
>     * 
> ====================================================================
>     * 
>   @@ -63,169 +63,130 @@
>    package org.apache.struts.util;
>    
>    
>   +import java.io.PrintWriter;
>   +import java.io.Serializable;
>    import java.text.MessageFormat;
>   -import java.util.Enumeration;
>   -import java.util.Hashtable;
>   +import java.util.HashMap;
>    import java.util.Locale;
>   -import java.util.MissingResourceException;
>   -import java.util.ResourceBundle;
>    
>    
>    /**
>   - * General purpose utility module that wraps the named 
> resource bundle
>   - * passed to our constructor and produces messages from 
> it, with parametric
>   - * replacement of MessageFormat parameters.  Convenience 
> methods allow
>   - * retrieval of messages for either the default Locale or 
> a specified Locale.
>   + * General purpose abstract class that describes an API 
> for retrieving
>   + * Locale-sensitive messages from underlying resource 
> locations of an
>   + * unspecified design, and optionally utilizing the 
> <code>MessageFormat</code>
>   + * class to produce internationalized messages with 
> parametric replacement.
>   + * <p>
>   + * Calls to <code>getMessage()</code> variants without a 
> <code>Locale</code>
>   + * argument are presumed to be requesting a message string 
> in the default
>   + * <code>Locale</code> for this JVM.
>   + * <p>
>   + * Calls to <code>getMessage()</code> with an unknown key, 
> or an unknown
>   + * <code>Locale</code> will return <code>null</code> if the
>   + * <code>returnNull</code> property is set to 
> <code>true</code>.  Otherwise,
>   + * a suitable error message will be returned instead.
>   + * <p>
>   + * <strong>IMPLEMENTATION NOTE</strong> - Classes that 
> extend this class
>   + * must be Serializable so that instances may be used in 
> distributable
>   + * application server environments.
>     *
>     * @author Craig R. McClanahan
>   - * @version $Revision: 1.4 $ $Date: 2000/08/01 20:04:02 $
>   + * @version $Revision: 1.5 $ $Date: 2000/12/15 03:08:10 $
>     */
>    
>   -public final class MessageResources {
>   +public abstract class MessageResources implements Serializable {
>    
>    
>   -    // 
> ----------------------------------------------------------- 
> Constructors
>   +    // 
> ------------------------------------------------------------- 
> Properties
>    
>    
>        /**
>   -     * Construct a new resources object that wraps the 
> named ResourceBundles.
>   -     *
>   -     * @param base Base name of the ResourceBundles to be wrapped
>   -     *
>   -     * @exception MissingResourceException if the 
> specified bundle cannot
>   -     *  be loaded
>   +     * The configuration parameter used to initialize this 
> MessageResources.
>         */
>   -    public MessageResources(String base) throws 
> MissingResourceException {
>   -
>   -	super();
>   -	this.base = base;
>   -	this.bundle = ResourceBundle.getBundle(base);
>   +    protected String config = null;
>    
>   +    public String getConfig() {
>   +        return (this.config);
>        }
>    
>    
>   -    // 
> ----------------------------------------------------- 
> Instance Variables
>   -
>   -
>   -    /**
>   -     * The base name of resource bundles used for 
> resolving requests for
>   -     * messages.
>   -     */
>   -    private String base = null;
>   -
>   -
>        /**
>   -     * The resource bundle to be used for resolving 
> requests for messages
>   -     * when no Locale is specified (i.e. the bundle that 
> will be used for
>   -     * the default Locale).
>   +     * The default Locale for our environment.
>         */
>   -    private ResourceBundle bundle = null;
>   +    protected Locale defaultLocale = Locale.getDefault();
>    
>    
>        /**
>   -     * The set of resource bundles from which we access 
> resources, keyed by
>   -     * the key computed in <code>getLocaleKey()</code>.
>   +     * The <code>MessageResourcesFactory</code> that 
> created this instance.
>         */
>   -    private Hashtable bundles = new Hashtable();
>   +    protected MessageResourcesFactory factory = null;
>    
>   -
>   -    /**
>   -     * The default Locale for our installation.
>   -     */
>   -    private static final Locale defaultLocale = 
> Locale.getDefault();
>   +    public MessageResourcesFactory getFactory() {
>   +        return (this.factory);
>   +    }
>    
>    
>        /**
>   -     * The set of previously created message format 
> objects, keyed by
>   -     * the key computed in <code>getResourceKey()</code>.
>   +     * The set of previously created MessageFormat 
> objects, keyed by the
>   +     * key computed in <code>messageKey()</code>.
>         */
>   -    private Hashtable formats = new Hashtable();
>   +    private HashMap formats = new HashMap();
>    
>    
>        /**
>   -     * Should we return <code>null</code> for message keys 
> that are not
>   -     * found (instead of an error code that includes the 
> offending key)?
>   +     * Should we return <code>null</code> instead of an 
> error message string
>   +     * if an unknown Locale or key is requested?
>         */
>   -    private boolean returnNull = true;
>   -
>   +    protected boolean returnNull = false;
>    
>   -    // 
> ------------------------------------------------------------- 
> Properties
>   -
>   -
>   -    /**
>   -     * Return the "return null" property.
>   -     */
>        public boolean getReturnNull() {
>   -
>   -	return (this.returnNull);
>   -
>   +        return (this.returnNull);
>        }
>    
>   -
>   -    /**
>   -     * Set the "return null" property.
>   -     *
>   -     * @param returnNull The new "return null" property
>   -     */
>        public void setReturnNull(boolean returnNull) {
>   -
>   -	this.returnNull = returnNull;
>   -
>   +        this.returnNull = returnNull;
>        }
>    
>   -
>   -    // 
> --------------------------------------------------------- 
> Public Methods
>    
>   +    // 
> ----------------------------------------------------------- 
> Constructors
>    
>    
>        /**
>   -     * Return the resource bundle that will be used to 
> resolve message
>   -     * requests for the default Locale.
>   +     * Construct a new MessageResources according to the 
> specified parameters.
>         *
>   -     * @exception MissingResourceException if a suitable 
> bundle cannot
>   -     *  be located
>   +     * @param factory The MessageResourcesFactory that created us
>   +     * @param config The configuration parameter for this 
> MessageResources
>         */
>   -    public ResourceBundle getBundle() throws 
> MissingResourceException {
>   +    public MessageResources(MessageResourcesFactory 
> factory, String config) {
>    
>   -	return (getBundle(null));
>   +        this(factory, config, false);
>    
>        }
>    
>    
>        /**
>   -     * Return the resource bundle that will be used to 
> resolve message
>   -     * requests for the specified Locale, if there is one. 
>  Otherwise,
>   -     * return <code>null</code>
>   -     *
>   -     * @param locale The locale used to select a resource bundle
>   +     * Construct a new MessageResources according to the 
> specified parameters.
>         *
>   -     * @exception MissingResourceException if a suitable 
> bundle cannot
>   -     *  be located
>   +     * @param factory The MessageResourcesFactory that created us
>   +     * @param config The configuration parameter for this 
> MessageResources
>   +     * @param returnNull The returnNull property we should 
> initialize with
>         */
>   -    public ResourceBundle getBundle(Locale locale)
>   -	throws MissingResourceException {
>   -
>   -	if (locale == null)
>   -	    return (bundle);
>   -	String localeKey = getLocaleKey(locale);
>   +    public MessageResources(MessageResourcesFactory 
> factory, String config,
>   +                         boolean returnNull) {
>    
>   -	ResourceBundle bundle = null;
>   -	synchronized (bundles) {
>   -	    bundle = (ResourceBundle) bundles.get(localeKey);
>   -	    if (bundle == null) {
>   -		bundle = ResourceBundle.getBundle(base, locale);
>   -		bundles.put(localeKey, bundle);
>   -	    }
>   -	}
>   -	return (bundle);
>   +        super();
>   +        this.factory = factory;
>   +        this.config = config;
>   +        this.returnNull = returnNull;
>    
>        }
>    
>    
>   +    // 
> --------------------------------------------------------- 
> Public Methods
>   +
>   +
>   +
>        /**
>         * Returns a text message for the specified key, for 
> the default Locale.
>   -     * A null string result will be returned by this 
> method if no relevant
>   -     * message resource is found.
>         *
>         * @param key The message key to look up
>         */
>   @@ -238,8 +199,7 @@
>    
>        /**
>         * Returns a text message after parametric replacement 
> of the specified
>   -     * parameter placeholders.  A null string result will 
> be returned by
>   -     * this method if no resource bundle has been configured.
>   +     * parameter placeholders.
>         *
>         * @param key The message key to look up
>         * @param args An array of replacement parameters for 
> placeholders
>   @@ -253,13 +213,12 @@
>    
>        /**
>         * Returns a text message after parametric replacement 
> of the specified
>   -     * parameter placeholders.  A null string result will 
> never be returned
>   -     * by this method.
>   +     * parameter placeholders.
>         *
>         * @param key The message key to look up
>         * @param arg0 The replacement for placeholder {0} in 
> the message
>         */
>   -    public String getMessage(String key, String arg0) {
>   +    public String getMessage(String key, Object arg0) {
>    
>    	return (getMessage((Locale) null, key, arg0));
>    
>   @@ -268,14 +227,13 @@
>    
>        /**
>         * Returns a text message after parametric replacement 
> of the specified
>   -     * parameter placeholders.  A null string result will 
> never be returned
>   -     * by this method.
>   +     * parameter placeholders.
>         *
>         * @param key The message key to look up
>         * @param arg0 The replacement for placeholder {0} in 
> the message
>         * @param arg1 The replacement for placeholder {1} in 
> the message
>         */
>   -    public String getMessage(String key, String arg0, 
> String arg1) {
>   +    public String getMessage(String key, Object arg0, 
> Object arg1) {
>    
>    	return (getMessage((Locale) null, key, arg0, arg1));
>    
>   @@ -284,16 +242,15 @@
>    
>        /**
>         * Returns a text message after parametric replacement 
> of the specified
>   -     * parameter placeholders.  A null string result will 
> never be returned
>   -     * by this method.
>   +     * parameter placeholders.
>         *
>         * @param key The message key to look up
>         * @param arg0 The replacement for placeholder {0} in 
> the message
>         * @param arg1 The replacement for placeholder {1} in 
> the message
>         * @param arg2 The replacement for placeholder {2} in 
> the message
>         */
>   -    public String getMessage(String key, String arg0, String arg1,
>   -			     String arg2) {
>   +    public String getMessage(String key, Object arg0, Object arg1,
>   +			     Object arg2) {
>    
>    	return (getMessage((Locale) null, key, arg0, arg1, arg2));
>    
>   @@ -302,8 +259,7 @@
>    
>        /**
>         * Returns a text message after parametric replacement 
> of the specified
>   -     * parameter placeholders.  A null string result will 
> never be returned
>   -     * by this method.
>   +     * parameter placeholders.
>         *
>         * @param key The message key to look up
>         * @param arg0 The replacement for placeholder {0} in 
> the message
>   @@ -311,8 +267,8 @@
>         * @param arg2 The replacement for placeholder {2} in 
> the message
>         * @param arg3 The replacement for placeholder {3} in 
> the message
>         */
>   -    public String getMessage(String key, String arg0, String arg1,
>   -			     String arg2, String arg3) {
>   +    public String getMessage(String key, Object arg0, Object arg1,
>   +			     Object arg2, Object arg3) {
>    
>    	return (getMessage((Locale) null, key, arg0, arg1, arg2, arg3));
>    
>   @@ -322,20 +278,19 @@
>        /**
>         * Returns a text message for the specified key, for 
> the default Locale.
>         * A null string result will be returned by this 
> method if no relevant
>   -     * message resource is found.
>   +     * message resource is found for this key or Locale, if the
>   +     * <code>returnNull</code> property is set.  
> Otherwise, an appropriate
>   +     * error message will be returned.
>   +     * <p>
>   +     * This method must be implemented by a concrete subclass.
>         *
>         * @param locale The requested message Locale, or 
> <code>null</code>
>         *  for the system default Locale
>         * @param key The message key to look up
>         */
>   -    public String getMessage(Locale locale, String key) {
>   +    public abstract String getMessage(Locale locale, String key);
>    
>   -	Object args[] = new Object[0];
>   -	return (getMessage(locale, key, args));
>    
>   -    }
>   -
>   -
>        /**
>         * Returns a text message after parametric replacement 
> of the specified
>         * parameter placeholders.  A null string result will 
> be returned by
>   @@ -349,20 +304,22 @@
>        public String getMessage(Locale locale, String key, 
> Object args[]) {
>    
>    	// Cache MessageFormat instances as they are accessed
>   +        if (locale == null)
>   +            locale = defaultLocale;
>    	MessageFormat format = null;
>   -	String messageKey = getResourceKey(locale, key);
>   +	String formatKey = messageKey(locale, key);
>    	synchronized (formats) {
>   -	    format = (MessageFormat) formats.get(messageKey);
>   +	    format = (MessageFormat) formats.get(formatKey);
>    	    if (format == null) {
>   -		String formatString = (String) getResource(locale, key);
>   +		String formatString = getMessage(locale, key);
>    		if (formatString == null) {
>    		    if (returnNull)
>    			return (null);
>    		    else
>   -			return ("???" + key + "???");
>   +			return ("???" + formatKey + "???");
>    		}
>    		format = new MessageFormat(formatString);
>   -		formats.put(messageKey, format);
>   +		formats.put(formatKey, format);
>    	    }
>    
>    	}
>   @@ -381,7 +338,7 @@
>         * @param key The message key to look up
>         * @param arg0 The replacement for placeholder {0} in 
> the message
>         */
>   -    public String getMessage(Locale locale, String key, 
> String arg0) {
>   +    public String getMessage(Locale locale, String key, 
> Object arg0) {
>    
>    	Object args[] = new Object[1];
>    	args[0] = arg0;
>   @@ -402,7 +359,7 @@
>         * @param arg1 The replacement for placeholder {1} in 
> the message
>         */
>        public String getMessage(Locale locale,
>   -			     String key, String arg0, String arg1) {
>   +			     String key, Object arg0, Object arg1) {
>    
>    	Object args[] = new Object[2];
>    	args[0] = arg0;
>   @@ -425,8 +382,8 @@
>         * @param arg2 The replacement for placeholder {2} in 
> the message
>         */
>        public String getMessage(Locale locale,
>   -			     String key, String arg0, String arg1,
>   -			     String arg2) {
>   +			     String key, Object arg0, Object arg1,
>   +			     Object arg2) {
>    
>    	Object args[] = new Object[3];
>    	args[0] = arg0;
>   @@ -451,8 +408,8 @@
>         * @param arg3 The replacement for placeholder {3} in 
> the message
>         */
>        public String getMessage(Locale locale,
>   -			     String key, String arg0, String arg1,
>   -			     String arg2, String arg3) {
>   +			     String key, Object arg0, Object arg1,
>   +			     Object arg2, Object arg3) {
>    
>    	Object args[] = new Object[4];
>    	args[0] = arg0;
>   @@ -464,121 +421,115 @@
>        }
>    
>    
>   +    // 
> ------------------------------------------------------ 
> Protected Methods
>   +
>   +
>        /**
>   -     * Returns a resource for the specified message key, 
> or <code>null</code>
>   -     * if no corresponding resource can be found, for the 
> default Locale.
>   +     * Compute and return a key to be used in caching 
> information by a Locale.
>   +     * <strong>NOTE</strong> - The locale key for the 
> default Locale in our
>   +     * environment is a zero length String.
>         *
>   -     * @param key The resource key to look up
>   +     * @param locale The locale for which a key is desired
>         */
>   -    public Object getResource(String key) {
>   +    protected String localeKey(Locale locale) {
>    
>   -	return (getResource((Locale) null, key));
>   +        if (locale == null)
>   +            return ("");
>   +        else if (locale.equals(defaultLocale))
>   +            return ("");
>   +        else
>   +            return (locale.toString());
>    
>        }
>    
>    
>        /**
>   -     * Returns a resource for the specified message key, 
> or <code>null</code>
>   -     * if no corresponding resource can be found, for the 
> specified Locale.
>   +     * Compute and return a key to be used in caching information
>   +     * by Locale and message key.
>         *
>   -     * @param locale The requested message Locale, or 
> <code>null</code>
>   -     *  for the system default Locale
>   -     * @param key The resource key to look up
>   +     * @param locale The Locale for which this format key 
> is calculated
>   +     * @param key The message key for which this format 
> key is calculated
>         */
>   -    public Object getResource(Locale locale, String key) {
>   +    protected String messageKey(Locale locale, String key) {
>    
>   -	// Look up the resource bundle we will use
>   -	ResourceBundle bundle = null;
>   -	try {
>   -	    bundle = getBundle(locale);
>   -	} catch (Exception e) {
>   -	    return (null);
>   -	}
>   -
>   -	// Look up the resource itself in this resource bundle
>   -	Object result = null;
>   -	try {
>   -	    result = bundle.getString(key);
>   -	} catch (MissingResourceException e) {
>   -	    return (null);
>   -	}
>   -	return (result);
>   +        return (localeKey(locale) + "." + key);
>    
>        }
>    
>    
>   -    // 
> -------------------------------------------------------- 
> Private Methods
>   -
>   -
>        /**
>   -     * Returns the key under which resources for a 
> particular Locale may be
>   -     * uniquely accessed.
>   +     * Compute and return a key to be used in caching information
>   +     * by locale key and message key.
>         *
>   -     * @param locale The locale for which a key is to be 
> computed, or
>   -     *  <code>null</code> for the system default Locale
>   +     * @param localeKey The locale key for which this 
> cache key is calculated
>   +     * @param key The message key for which this cache key 
> is calculated
>         */
>   -    private String getLocaleKey(Locale locale) {
>   +    protected String messageKey(String localeKey, String key) {
>    
>   -	if (locale == null)
>   -	    locale = defaultLocale;
>   -	StringBuffer sb = new StringBuffer();
>   -	sb.append(locale.getCountry());
>   -	sb.append(".");
>   -	sb.append(locale.getLanguage());
>   -	sb.append(".");
>   -	String variant = locale.getVariant();
>   -	if (variant != null) {
>   -	    sb.append(variant);
>   -	    sb.append(".");
>   -	}
>   -	return (sb.toString());
>   +        return (localeKey + "." + key);
>    
>        }
>    
>    
>   +    // 
> --------------------------------------------------------- 
> Static Methods
>   +
>   +
>        /**
>   -     * Returns the key under which a message format for a 
> particular message,
>   -     * from the resource bundle for a particular locale, 
> may be uniquely
>   -     * accessed.
>   -     *
>   -     * @param locale Locale for which this message key is computed
>   -     * @param id Message identifier for which this message 
> key is computed
>   +     * The default MessageResourcesFactory used to create 
> MessageResources
>   +     * instances.
>         */
>   -    private String getResourceKey(Locale locale, String id) {
>   +    protected static MessageResourcesFactory defaultFactory = null;
>    
>   -	return (getLocaleKey(locale) + "." + id);
>    
>   -    }
>   +    /**
>   +     * Create and return an instance of 
> <code>MessageResources</code> for the
>   +     * created by the default <code>MessageResourcesFactory</code>.
>   +     *
>   +     * @param config Configuration parameter for this 
> message bundle.
>   +     */
>   +    public synchronized static MessageResources
>   +        getMessageResources(String config) {
>    
>   +        if (defaultFactory == null)
>   +            defaultFactory = 
> MessageResourcesFactory.createFactory();
>   +        return defaultFactory.createResources(config);
>    
>   -    // 
> --------------------------------------------------------- 
> Static Methods
>   +    }
>    
>    
>        /**
>   -     * The MessageResources instances that have been 
> created so far,
>   -     * keyed by base name.
>   +     * Log a message to the Writer that has been 
> configured for our use.
>   +     *
>   +     * @param message The message to be logged
>         */
>   -    private static Hashtable cache = new Hashtable();
>   +    public void log(String message) {
>    
>   +        PrintWriter writer = factory.getWriter();
>   +        if (writer != null) {
>   +            writer.print("MessageResources: ");
>   +            writer.println(message);
>   +            writer.flush();
>   +        }
>    
>   +    }
>   +
>   +
>        /**
>   -     * Return an instance of MessageResources for the 
> specified base name.
>   +     * Log a message and exception to the Writer that has 
> been configured
>   +     * for our use.
>         *
>   -     * @param base Base name of the ResourceBundles to be wrapped
>   -     *
>   -     * @exception MissingResourcesException if no default resources
>   -     *  can be loaded
>   +     * @param message The message to be logged
>   +     * @param throwable The exception to be logged
>         */
>   -    public synchronized static MessageResources
>   -	getMessageResources(String base) throws 
> MissingResourceException {
>   +    public void log(String message, Throwable throwable) {
>    
>   -	MessageResources messageResources =
>   -	    (MessageResources) cache.get(base);
>   -	if (messageResources != null)
>   -	    return (messageResources);
>   -	messageResources = new MessageResources(base);
>   -	cache.put(base, messageResources);
>   -	return (messageResources);
>   +        PrintWriter writer = factory.getWriter();
>   +        if (writer != null) {
>   +            writer.print("MessageResources: ");
>   +            writer.println(message);
>   +            throwable.printStackTrace(writer);
>   +            writer.flush();
>   +        }
>    
>        }
>    
>   
>   
>   
>   1.1                  
> jakarta-struts/src/share/org/apache/struts/util/MessageResourc
> esFactory.java
>   
>   Index: MessageResourcesFactory.java
>   ===================================================================
>   /*
>    * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/util/Mess
> ageResourcesFactory.java,v 1.1 2000/12/15 03:08:11 craigmcc Exp $
>    * $Revision: 1.1 $
>    * $Date: 2000/12/15 03:08:11 $
>    *
>    * 
> ====================================================================
>    * 
>    * The Apache Software License, Version 1.1
>    *
>    * Copyright (c) 1999 The Apache Software Foundation.  All rights 
>    * reserved.
>    *
>    * Redistribution and use in source and binary forms, with 
> or without
>    * modification, are permitted provided that the following 
> conditions
>    * are met:
>    *
>    * 1. Redistributions of source code must retain the above copyright
>    *    notice, this list of conditions and the following disclaimer. 
>    *
>    * 2. Redistributions in binary form must reproduce the 
> above copyright
>    *    notice, this list of conditions and the following 
> disclaimer in
>    *    the documentation and/or other materials provided with the
>    *    distribution.
>    *
>    * 3. The end-user documentation included with the 
> redistribution, if
>    *    any, must include the following acknowlegement:  
>    *       "This product includes software developed by the 
>    *        Apache Software Foundation (http://www.apache.org/)."
>    *    Alternately, this acknowlegement may appear in the 
> software itself,
>    *    if and wherever such third-party acknowlegements 
> normally appear.
>    *
>    * 4. The names "The Jakarta Project", "Tomcat", and 
> "Apache Software
>    *    Foundation" must not be used to endorse or promote 
> products derived
>    *    from this software without prior written permission. 
> For written 
>    *    permission, please contact apache@apache.org.
>    *
>    * 5. Products derived from this software may not be called "Apache"
>    *    nor may "Apache" appear in their names without prior written
>    *    permission of the Apache Group.
>    *
>    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
> CAUSED AND
>    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
> LIABILITY,
>    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
> ANY WAY OUT
>    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
> POSSIBILITY OF
>    * SUCH DAMAGE.
>    * 
> ====================================================================
>    *
>    * This software consists of voluntary contributions made by many
>    * individuals on behalf of the Apache Software Foundation. 
>  For more
>    * information on the Apache Software Foundation, please see
>    * <http://www.apache.org/>.
>    *
>    */ 
>   
>   
>   package org.apache.struts.util;
>   
>   
>   import java.io.PrintWriter;
>   
>   
>   /**
>    * Factory for <code>MessageResources</code> instances.  
> The general usage
>    * pattern for this class is:
>    * <ul>
>    * <li>Call 
> <code>MessageResourcesFactory().createFactory()</code> to retrieve
>    *     a <code>MessageResourcesFactory</code> instance.</li>
>    * <li>Set properties as required to configure this factory 
> instance to create
>    *     <code>MessageResources</code> instances with desired
>    *     characteristics.</li>
>    * <li>Call the <code>createResources()</code> method of 
> the factory to
>    *     retrieve a newly instantiated <code>MessageResources</code>
>    *     instance.</li>
>    * </ul>
>    *
>    * @author Craig R. McClanahan
>    * @version $Revision: 1.1 $ $Date: 2000/12/15 03:08:11 $
>    */
>   
>   public abstract class MessageResourcesFactory {
>   
>   
>       // ---------------------------------------------------- 
> Instance Properties
>   
>   
>       /**
>        * The "return null" property value to which newly created
>        * MessageResourcess should be initialized.
>        */
>       protected boolean returnNull = true;
>   
>       public boolean getReturnNull() {
>           return (this.returnNull);
>       }
>   
>       public void setReturnNull(boolean returnNull) {
>           this.returnNull = returnNull;
>       }
>   
>   
>       /**
>        * The PrintWriter to which we can log debugging and 
> error information,
>        * if any.
>        */
>       protected PrintWriter writer = null;
>   
>       public PrintWriter getWriter() {
>           return (this.writer);
>       }
>   
>       public void setWriter(PrintWriter writer) {
>           this.writer = writer;
>       }
>   
>   
>       // 
> --------------------------------------------------------- 
> Public Methods
>   
>   
>       /**
>        * Create and return a newly instansiated 
> <code>MessageResources</code>.
>        * This method must be implemented by concrete subclasses.
>        *
>        * @param config Configuration parameter(s) for the 
> requested bundle
>        */
>       public abstract MessageResources createResources(String config);
>   
>   
>       // 
> ------------------------------------------------------ Static 
> Properties
>   
>   
>       /**
>        * The Java class to be used for
>        * <code>MessageResourcesFactory</code> instances.
>        */
>       protected static Class clazz = null;
>   
>   
>       /**
>        * The fully qualified class name to be used for
>        * <code>MessageResourcesFactory</code> instances.
>        */
>       protected static String factoryClass =
>           "org.apache.struts.util.PropertyMessageResourcesFactory";
>   
>       public static String getFactoryClass() {
>           return (MessageResourcesFactory.factoryClass);
>       }
>   
>       public static void setFactoryClass(String factoryClass) {
>           MessageResourcesFactory.factoryClass = factoryClass;
>           MessageResourcesFactory.clazz = null;
>       }
>   
>   
>       /**
>        * The PrintWriter to which we can log error and 
> debugging information,
>        * if any.
>        */
>       protected static PrintWriter defaultWriter = null;
>   
>       public static PrintWriter getDefaultWriter() {
>           return (MessageResourcesFactory.defaultWriter);
>       }
>   
>       public static void setDefaultWriter(PrintWriter writer) {
>           MessageResourcesFactory.defaultWriter = writer;
>       }
>   
>   
>       // 
> --------------------------------------------------------- 
> Static Methods
>   
>   
>       /**
>        * Create and return a 
> <code>MessageResourcesFactory</code> instance of the
>        * appropriate class, which can be used to create customized
>        * <code>MessageResources</code> instances.  If no such 
> factory can be
>        * created, return <code>null</code> instead.
>        */
>       public static MessageResourcesFactory createFactory() {
>   
>           // Construct a new instance of the specified factory class
>           try {
>               if (clazz == null)
>                   clazz = Class.forName(factoryClass);
>               MessageResourcesFactory factory =
>                   (MessageResourcesFactory) clazz.newInstance();
>               factory.setWriter(defaultWriter);
>               return (factory);
>           } catch (Throwable t) {
>               if (defaultWriter != null) {
>                   
> defaultWriter.println("MessageResourcesFactory.createFactory");
>                   t.printStackTrace(defaultWriter);
>                   defaultWriter.flush();
>               }
>               return (null);
>           }
>   
>       }
>   
>   
>   }
>   
>   
>   
>   1.1                  
> jakarta-struts/src/share/org/apache/struts/util/PropertyMessag
> eResources.java
>   
>   Index: PropertyMessageResources.java
>   ===================================================================
>   /*
>    * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/util/Prop
> ertyMessageResources.java,v 1.1 2000/12/15 03:08:11 craigmcc Exp $
>    * $Revision: 1.1 $
>    * $Date: 2000/12/15 03:08:11 $
>    *
>    * 
> ====================================================================
>    * 
>    * The Apache Software License, Version 1.1
>    *
>    * Copyright (c) 1999 The Apache Software Foundation.  All rights 
>    * reserved.
>    *
>    * Redistribution and use in source and binary forms, with 
> or without
>    * modification, are permitted provided that the following 
> conditions
>    * are met:
>    *
>    * 1. Redistributions of source code must retain the above copyright
>    *    notice, this list of conditions and the following disclaimer. 
>    *
>    * 2. Redistributions in binary form must reproduce the 
> above copyright
>    *    notice, this list of conditions and the following 
> disclaimer in
>    *    the documentation and/or other materials provided with the
>    *    distribution.
>    *
>    * 3. The end-user documentation included with the 
> redistribution, if
>    *    any, must include the following acknowlegement:  
>    *       "This product includes software developed by the 
>    *        Apache Software Foundation (http://www.apache.org/)."
>    *    Alternately, this acknowlegement may appear in the 
> software itself,
>    *    if and wherever such third-party acknowlegements 
> normally appear.
>    *
>    * 4. The names "The Jakarta Project", "Tomcat", and 
> "Apache Software
>    *    Foundation" must not be used to endorse or promote 
> products derived
>    *    from this software without prior written permission. 
> For written 
>    *    permission, please contact apache@apache.org.
>    *
>    * 5. Products derived from this software may not be called "Apache"
>    *    nor may "Apache" appear in their names without prior written
>    *    permission of the Apache Group.
>    *
>    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
> CAUSED AND
>    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
> LIABILITY,
>    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
> ANY WAY OUT
>    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
> POSSIBILITY OF
>    * SUCH DAMAGE.
>    * 
> ====================================================================
>    *
>    * This software consists of voluntary contributions made by many
>    * individuals on behalf of the Apache Software Foundation. 
>  For more
>    * information on the Apache Software Foundation, please see
>    * <http://www.apache.org/>.
>    *
>    */ 
>   
>   
>   package org.apache.struts.util;
>   
>   
>   import java.io.InputStream;
>   import java.util.Enumeration;
>   import java.util.HashMap;
>   import java.util.Locale;
>   import java.util.Properties;
>   
>   
>   /**
>    * Concrete subclass of <code>MessageResources</code> that 
> reads message keys
>    * and corresponding strings from named property resources 
> in the same manner
>    * that <code>java.util.PropertyResourceBundle</code> does.  The
>    * <code>base</code> property defines the base property 
> resource name, and
>    * must be specified.
>    * <p>
>    * <strong>IMPLEMENTATION NOTE</strong> - This class trades 
> memory for
>    * speed by caching all messages located via generalizing 
> the Locale under
>    * the original locale as well.
>    * This results in specific messages being stored in the 
> message cache
>    * more than once, but improves response time on subsequent 
> requests for
>    * the same locale + key combination.
>    *
>    * @author Craig R. McClanahan
>    * @version $Revision: 1.1 $ $Date: 2000/12/15 03:08:11 $
>    */
>   
>   public class PropertyMessageResources extends MessageResources {
>   
>   
>       // 
> ----------------------------------------------------------- 
> Constructors
>   
>   
>       /**
>        * Construct a new PropertyMessageResources according to the
>        * specified parameters.
>        *
>        * @param factory The MessageResourcesFactory that created us
>        * @param config The configuration parameter for this 
> MessageResources
>        */
>       public PropertyMessageResources(MessageResourcesFactory factory,
>                                       String config) {
>   
>           super(factory, config);
>   
>       }
>   
>   
>       /**
>        * Construct a new PropertyMessageResources according to the
>        * specified parameters.
>        *
>        * @param factory The MessageResourcesFactory that created us
>        * @param config The configuration parameter for this 
> MessageResources
>        * @param returnNull The returnNull property we should 
> initialize with
>        */
>       public PropertyMessageResources(MessageResourcesFactory factory,
>                                       String config, boolean 
> returnNull) {
>   
>           super(factory, config, returnNull);
>   
>       }
>   
>   
>       // 
> ------------------------------------------------------------- 
> Properties
>   
>   
>       /**
>        * The set of locale keys for which we have already 
> loaded messages, keyed
>        * by the value calculated in <code>localeKey()</code>.
>        */
>       protected HashMap locales = new HashMap();
>   
>   
>       /**
>        * The cache of messages we have accumulated over time, 
> keyed by the
>        * value calculated in <code>messageKey()</code>.
>        */
>       protected HashMap messages = new HashMap();
>   
>   
>       // 
> --------------------------------------------------------- 
> Public Methods
>   
>   
>       /**
>        * Returns a text message for the specified key, for 
> the default Locale.
>        * A null string result will be returned by this method 
> if no relevant
>        * message resource is found for this key or Locale, if the
>        * <code>returnNull</code> property is set.  Otherwise, 
> an appropriate
>        * error message will be returned.
>        * <p>
>        * This method must be implemented by a concrete subclass.
>        *
>        * @param locale The requested message Locale, or 
> <code>null</code>
>        *  for the system default Locale
>        * @param key The message key to look up
>        */
>       public String getMessage(Locale locale, String key) {
>   
>           // Initialize variables we will require
>           String localeKey = localeKey(locale);
>           String originalKey = messageKey(localeKey, key);
>           String messageKey = null;
>           String message = null;
>           int underscore = 0;
>           boolean addIt = false;  // Add if not found under 
> the original key
>   
>           // Loop from specific to general Locales looking 
> for this message
>           while (true) {
>   
>               // Load this Locale's messages if we have not 
> done so yet
>               loadLocale(localeKey);
>   
>               // Check if we have this key for the current locale key
>               messageKey = messageKey(localeKey, key);
>               synchronized (messages) {
>                   message = (String) messages.get(messageKey);
>                   if (message != null) {
>                       if (addIt)
>                           messages.put(originalKey, message);
>                       return (message);
>                   }
>               }
>   
>               // Strip trailing modifiers to try a more 
> general locale key
>               addIt = true;
>               underscore = localeKey.lastIndexOf("_");
>               if (underscore < 0)
>                   break;
>               localeKey = localeKey.substring(0, underscore);
>   
>           }
>   
>           // As a last resort, try the default Locale
>           localeKey = localeKey(defaultLocale);
>           messageKey = messageKey(localeKey, key);
>           loadLocale(localeKey);
>           synchronized (messages) {
>               message = (String) messages.get(messageKey);
>               if (message != null) {
>                   if (addIt)
>                       messages.put(originalKey, message);
>                   return (message);
>               }
>           }
>   
>           // Return an appropriate error indication
>           if (returnNull)
>               return (null);
>           else
>               return ("???" + messageKey(locale, key) + "???");
>   
>       }
>   
>   
>       // 
> ------------------------------------------------------ 
> Protected Methods
>   
>   
>       /**
>        * Load the messages associated with the specified 
> Locale key.  For this
>        * implementation, the <code>config</code> property 
> should contain a fully
>        * qualified package and resource name, separated by 
> periods, of a series
>        * of property resources to be loaded from the class 
> loader that created
>        * this PropertyMessageResources instance.  This is 
> exactly the same name
>        * format you would use when utilizing the
>        * <code>java.util.PropertyResourceBundle</code> class.
>        *
>        * @param localeKey Locale key for the messages to be retrieved
>        */
>       protected void loadLocale(String localeKey) {
>   
>           // Have we already attempted to load messages for 
> this locale?
>           synchronized (locales) {
>               if (locales.get(localeKey) != null)
>                   return;
>               locales.put(localeKey, localeKey);
>           }
>   
>           // Set up to load the property resource for this 
> locale key, if we can
>           String name = config.replace('.', '/');
>           if (localeKey.length() > 0)
>               name += "_" + localeKey;
>           name += ".properties";
>           InputStream is = null;
>           Properties props = new Properties();
>   
>           // Load the specified property resource
>           try {
>               is = 
> this.getClass().getClassLoader().getResourceAsStream(name);
>               if (is != null) {
>                   props.load(is);
>                   is.close();
>               }
>           } catch (Throwable t) {
>               if (is != null) {
>                   try {
>                       is.close();
>                   } catch (Throwable u) {
>                       ;
>                   }
>               }
>           }
>   
>           // Copy the corresponding values into our cache
>           if (props.size() < 1)
>               return;
>           synchronized (messages) {
>               Enumeration names = props.keys();
>               while (names.hasMoreElements()) {
>                   String key = (String) names.nextElement();
>                   messages.put(messageKey(localeKey, key),
>                                props.getProperty(key));
>               }
>           }
>   
>       }
>   
>   
>   }
>   
>   
>   
>   1.1                  
> jakarta-struts/src/share/org/apache/struts/util/PropertyMessag
> eResourcesFactory.java
>   
>   Index: PropertyMessageResourcesFactory.java
>   ===================================================================
>   /*
>    * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/util/Prop
> ertyMessageResourcesFactory.java,v 1.1 2000/12/15 03:08:11 
> craigmcc Exp $
>    * $Revision: 1.1 $
>    * $Date: 2000/12/15 03:08:11 $
>    *
>    * 
> ====================================================================
>    * 
>    * The Apache Software License, Version 1.1
>    *
>    * Copyright (c) 1999 The Apache Software Foundation.  All rights 
>    * reserved.
>    *
>    * Redistribution and use in source and binary forms, with 
> or without
>    * modification, are permitted provided that the following 
> conditions
>    * are met:
>    *
>    * 1. Redistributions of source code must retain the above copyright
>    *    notice, this list of conditions and the following disclaimer. 
>    *
>    * 2. Redistributions in binary form must reproduce the 
> above copyright
>    *    notice, this list of conditions and the following 
> disclaimer in
>    *    the documentation and/or other materials provided with the
>    *    distribution.
>    *
>    * 3. The end-user documentation included with the 
> redistribution, if
>    *    any, must include the following acknowlegement:  
>    *       "This product includes software developed by the 
>    *        Apache Software Foundation (http://www.apache.org/)."
>    *    Alternately, this acknowlegement may appear in the 
> software itself,
>    *    if and wherever such third-party acknowlegements 
> normally appear.
>    *
>    * 4. The names "The Jakarta Project", "Tomcat", and 
> "Apache Software
>    *    Foundation" must not be used to endorse or promote 
> products derived
>    *    from this software without prior written permission. 
> For written 
>    *    permission, please contact apache@apache.org.
>    *
>    * 5. Products derived from this software may not be called "Apache"
>    *    nor may "Apache" appear in their names without prior written
>    *    permission of the Apache Group.
>    *
>    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
> CAUSED AND
>    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
> LIABILITY,
>    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
> ANY WAY OUT
>    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
> POSSIBILITY OF
>    * SUCH DAMAGE.
>    * 
> ====================================================================
>    *
>    * This software consists of voluntary contributions made by many
>    * individuals on behalf of the Apache Software Foundation. 
>  For more
>    * information on the Apache Software Foundation, please see
>    * <http://www.apache.org/>.
>    *
>    */ 
>   
>   
>   package org.apache.struts.util;
>   
>   
>   /**
>    * Factory for <code>PropertyMessageResources</code> instances.  The
>    * configuration paramter for such instances is the base 
> Java package
>    * name of the resources entries from which our keys and 
> values will be
>    * loaded.
>    *
>    * @author Craig R. McClanahan
>    * @version $Revision: 1.1 $ $Date: 2000/12/15 03:08:11 $
>    */
>   
>   public class PropertyMessageResourcesFactory extends 
> MessageResourcesFactory {
>   
>   
>       // 
> --------------------------------------------------------- 
> Public Methods
>   
>   
>       /**
>        * Create and return a newly instansiated 
> <code>MessageResources</code>.
>        * This method must be implemented by concrete subclasses.
>        *
>        * @param config Configuration parameter(s) for the 
> requested bundle
>        */
>       public MessageResources createResources(String config) {
>   
>           return new PropertyMessageResources(this, config, 
> this.returnNull);
>   
>       }
>   
>   
>   }
>   
>   
>   
>   1.1                  
> jakarta-struts/src/share/org/apache/struts/util/ServletContext
> Writer.java
>   
>   Index: ServletContextWriter.java
>   ===================================================================
>   /*
>    * $Header: 
> /home/cvs/jakarta-struts/src/share/org/apache/struts/util/Serv
> letContextWriter.java,v 1.1 2000/12/15 03:08:11 craigmcc Exp $
>    * $Revision: 1.1 $
>    * $Date: 2000/12/15 03:08:11 $
>    *
>    * 
> ====================================================================
>    * 
>    * The Apache Software License, Version 1.1
>    *
>    * Copyright (c) 1999 The Apache Software Foundation.  All rights 
>    * reserved.
>    *
>    * Redistribution and use in source and binary forms, with 
> or without
>    * modification, are permitted provided that the following 
> conditions
>    * are met:
>    *
>    * 1. Redistributions of source code must retain the above copyright
>    *    notice, this list of conditions and the following disclaimer. 
>    *
>    * 2. Redistributions in binary form must reproduce the 
> above copyright
>    *    notice, this list of conditions and the following 
> disclaimer in
>    *    the documentation and/or other materials provided with the
>    *    distribution.
>    *
>    * 3. The end-user documentation included with the 
> redistribution, if
>    *    any, must include the following acknowlegement:  
>    *       "This product includes software developed by the 
>    *        Apache Software Foundation (http://www.apache.org/)."
>    *    Alternately, this acknowlegement may appear in the 
> software itself,
>    *    if and wherever such third-party acknowlegements 
> normally appear.
>    *
>    * 4. The names "The Jakarta Project", "Tomcat", and 
> "Apache Software
>    *    Foundation" must not be used to endorse or promote 
> products derived
>    *    from this software without prior written permission. 
> For written 
>    *    permission, please contact apache@apache.org.
>    *
>    * 5. Products derived from this software may not be called "Apache"
>    *    nor may "Apache" appear in their names without prior written
>    *    permission of the Apache Group.
>    *
>    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
> CAUSED AND
>    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
> LIABILITY,
>    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
> ANY WAY OUT
>    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
> POSSIBILITY OF
>    * SUCH DAMAGE.
>    * 
> ====================================================================
>    *
>    * This software consists of voluntary contributions made by many
>    * individuals on behalf of the Apache Software Foundation. 
>  For more
>    * information on the Apache Software Foundation, please see
>    * <http://www.apache.org/>.
>    *
>    */ 
>   
>   
>   package org.apache.struts.util;
>   
>   
>   import java.io.PrintWriter;
>   import java.io.StringWriter;
>   import javax.servlet.ServletContext;
>   
>   
>   /**
>    * A PrintWriter implementation that uses the logging 
> facilities of a
>    * <code>javax.servlet.ServletContext</code> to output its 
> results.  Output
>    * will be buffered until a newline character is output, 
> <code>flush()</code>
>    * is called, or until one of the <code>println()</code> 
> methods is called.
>    * Along the way, carriage return characters are skipped.
>    *
>    * @author Craig R. McClanahan
>    * @version $Revision: 1.1 $ $Date: 2000/12/15 03:08:11 $
>    */
>   
>   public class ServletContextWriter extends PrintWriter {
>   
>   
>       // 
> ----------------------------------------------------------- 
> Constructors
>   
>   
>       /**
>        * Construct a ServletContextWriter associated with the 
> specified
>        * ServletContext instance.
>        *
>        * @param context The associated servlet context
>        */
>       public ServletContextWriter(ServletContext context) {
>   
>           super(new StringWriter());
>           this.context = context;
>   
>       }
>   
>   
>       // 
> ------------------------------------------------------------- 
> Properties
>   
>   
>       /**
>        * The buffer into which we accumulate lines to be logged.
>        */
>       protected StringBuffer buffer = new StringBuffer();
>   
>   
>       /**
>        * The servlet context with which we are associated.
>        */
>       protected ServletContext context = null;
>   
>   
>       /**
>        * The error state for this stream.
>        */
>       protected boolean error = false;
>   
>   
>       // 
> --------------------------------------------------------- 
> Public Methods
>   
>   
>       /**
>        * Flush the stream and check for its error state.  
> <strong>IMPLEMENTATION
>        * NOTE</strong> - our associated servlet context gives 
> no indication of
>        * problems with logging, so the only way this method 
> will return
>        * <code>true</code> is if <code>setError()</code> is called.
>        */
>       public boolean checkError() {
>   
>           flush();
>           return (error);
>   
>       }
>   
>   
>       /**
>        * Close the stream.
>        */
>       public void close() {
>   
>           flush();
>   
>       }
>   
>   
>       /**
>        * Flush the stream.
>        */
>       public void flush() {
>   
>           if (buffer.length() > 0) {
>               context.log(buffer.toString());
>               buffer.setLength(0);
>           }
>   
>       }
>   
>   
>       /**
>        * Print a boolean value.
>        *
>        * @param b The value to be printed
>        */
>       public void print(boolean b) {
>   
>           write(String.valueOf(b));
>   
>       }
>   
>   
>       /**
>        * Print a character value.
>        *
>        * @param c The value to be printed
>        */
>       public void print(char c) {
>   
>           write(c);
>   
>       }
>   
>   
>       /**
>        * Print a character array.
>        *
>        * @param c The character array to be printed
>        */
>       public void print(char c[]) {
>   
>           for (int i = 0; i < c.length; i++)
>               write(c[i]);
>   
>       }
>   
>   
>       /**
>        * Print a double value.
>        *
>        * @param d The value to be printed
>        */
>       public void print(double d) {
>   
>           write(String.valueOf(d));
>   
>       }
>   
>   
>       /**
>        * Print a float value.
>        *
>        * @param f The value to be printed
>        */
>       public void print(float f) {
>   
>           write(String.valueOf(f));
>   
>       }
>   
>   
>       /**
>        * Print an integer value.
>        *
>        * @param i The value to be printed
>        */
>       public void print(int i) {
>   
>           write(String.valueOf(i));
>   
>       }
>   
>   
>       /**
>        * Print a long value.
>        *
>        * @param l The value to be printed
>        */
>       public void print(long l) {
>   
>           write(String.valueOf(l));
>   
>       }
>   
>   
>       /**
>        * Print an object.
>        *
>        * @param o The value to be printed
>        */
>       public void print(Object o) {
>   
>           write(o.toString());
>   
>       }
>   
>   
>       /**
>        * Print a String value.
>        *
>        * @param s The value to be printed
>        */
>       public void print(String s) {
>   
>           int len = s.length();
>           for (int i = 0; i < len; i++)
>               write(s.charAt(i));
>   
>       }
>   
>   
>       /**
>        * Terminate the current line and flush the buffer.
>        */
>       public void println() {
>   
>           flush();
>   
>       }
>   
>   
>       /**
>        * Print a boolean value and terminate the line.
>        *
>        * @param b The value to be printed
>        */
>       public void println(boolean b) {
>   
>           println(String.valueOf(b));
>   
>       }
>   
>   
>       /**
>        * Print a character value and terminate the line.
>        *
>        * @param c The value to be printed
>        */
>       public void println(char c) {
>   
>           write(c);
>           println();
>   
>       }
>   
>   
>       /**
>        * Print a character array and terminate the line.
>        *
>        * @param c The character array to be printed
>        */
>       public void println(char c[]) {
>   
>           for (int i = 0; i < c.length; i++)
>               print(c[i]);
>           println();
>   
>       }
>   
>   
>       /**
>        * Print a double value and terminate the line.
>        *
>        * @param d The value to be printed
>        */
>       public void println(double d) {
>   
>           println(String.valueOf(d));
>   
>       }
>   
>   
>       /**
>        * Print a float value and terminate the line.
>        *
>        * @param f The value to be printed
>        */
>       public void println(float f) {
>   
>           println(String.valueOf(f));
>   
>       }
>   
>   
>       /**
>        * Print an integer value and terminate the line.
>        *
>        * @param i The value to be printed
>        */
>       public void println(int i) {
>   
>           println(String.valueOf(i));
>   
>       }
>   
>   
>       /**
>        * Print a long value and terminate the line.
>        *
>        * @param l The value to be printed
>        */
>       public void println(long l) {
>   
>           println(String.valueOf(l));
>   
>       }
>   
>   
>       /**
>        * Print an object and terminate the line.
>        *
>        * @param o The value to be printed
>        */
>       public void println(Object o) {
>   
>           println(o.toString());
>   
>       }
>   
>   
>       /**
>        * Print a String value and terminate the line.
>        *
>        * @param s The value to be printed
>        */
>       public void println(String s) {
>   
>           int len = s.length();
>           for (int i = 0; i < len; i++)
>               print(s.charAt(i));
>           println();
>   
>       }
>   
>   
>       /**
>        * Set the error state for this stream.
>        */
>       public void setError() {
>   
>           this.error = true;
>   
>       }
>   
>   
>       /**
>        * Write a single character to this stream.
>        *
>        * @param c The character to be written
>        */
>       public void write(char c) {
>   
>           if (c == '\n')
>               flush();
>           else if (c != '\r')
>               buffer.append(c);
>   
>       }
>   
>   
>       /**
>        * Write a single character to this stream.
>        *
>        * @param c The character to be written
>        */
>       public void write(int c) {
>   
>           write((char) c);
>   
>       }
>   
>   
>       /**
>        * Write an array of charaters to this stream.
>        *
>        * @param buf The character array to be written
>        */
>       public void write(char buf[]) {
>   
>           for (int i = 0; i < buf.length; i++)
>               write(buf[i]);
>   
>       }
>   
>   
>       /**
>        * Write the specified subset of an array of characters 
> to this stream.
>        *
>        * @param buf The character array from which to write
>        * @param off The zero-relative starting offset to write
>        * @param len The number of characters to write
>        */
>       public void write(char buf[], int off, int len) {
>   
>           for (int i = off; i < len; i++)
>               write(buf[i]);
>   
>       }
>   
>   
>       /**
>        * Write a String to this stream.
>        *
>        * @param s The string to be written
>        */
>       public void write(String s) {
>   
>           int len = s.length();
>           for (int i = 0; i < len; i++)
>               write(s.charAt(i));
>   
>       }
>   
>   
>       /**
>        * Write the specified portion of a String to this stream.
>        *
>        * @param s The String from which to write
>        * @param off The zero-relative starting offset to write
>        * @param len The number of characters to write
>        */
>       public void write(String s, int off, int len) {
>   
>           for (int i = off; i < len; i++)
>               write(s.charAt(i));
>   
>       }
>   
>   
>   }
>   
>   
>   
>