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/15 04:08:13 UTC

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/ActionServlet.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/ActionServlet.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/ActionServlet.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/MessageResources.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/MessageResources.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/MessageResources.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/MessageResourcesFactory.java
  
  Index: MessageResourcesFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/MessageResourcesFactory.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/PropertyMessageResources.java
  
  Index: PropertyMessageResources.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/PropertyMessageResources.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/PropertyMessageResourcesFactory.java
  
  Index: PropertyMessageResourcesFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/PropertyMessageResourcesFactory.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/ServletContextWriter.java
  
  Index: ServletContextWriter.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/ServletContextWriter.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));
  
      }
  
  
  }