You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/11/08 22:14:14 UTC

[tomcat] branch 7.0.x updated (c88cf8f -> a354f2d)

This is an automated email from the ASF dual-hosted git repository.

markt pushed a change to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git.


    from c88cf8f  Improve clean-up after an OOME during request processing
     new 661f583  Align with 8.5.x/9.0.x
     new a354f2d  Fix some Javadoc warnings with Java 13

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 java/org/apache/catalina/ha/backend/Sender.java    |  10 +-
 java/org/apache/catalina/ha/backend/TcpSender.java |   1 +
 .../apache/catalina/tribes/ChannelReceiver.java    |   3 +-
 .../catalina/tribes/transport/ReceiverBase.java    |  25 +--
 .../tribes/transport/bio/BioReplicationTask.java   |   9 +-
 .../catalina/tribes/transport/bio/BioSender.java   |  26 +--
 .../apache/catalina/tribes/util/StringManager.java | 177 +++++++++++++++++----
 java/org/apache/tomcat/util/buf/ByteChunk.java     |  23 ++-
 java/org/apache/tomcat/util/buf/MessageBytes.java  |  84 ++++++----
 9 files changed, 251 insertions(+), 107 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 02/02: Fix some Javadoc warnings with Java 13

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit a354f2d6dcf6c8ba5ecedbeb6b42a5d6dd5d986c
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Nov 8 22:13:59 2019 +0000

    Fix some Javadoc warnings with Java 13
    
    Generally, fix by aligning with 8.5.x/9.0.x. Also include some
    additional minor clean-up
---
 java/org/apache/catalina/ha/backend/Sender.java    | 10 ++-
 java/org/apache/catalina/ha/backend/TcpSender.java |  1 +
 .../apache/catalina/tribes/ChannelReceiver.java    |  3 +-
 .../catalina/tribes/transport/ReceiverBase.java    | 25 +++----
 .../tribes/transport/bio/BioReplicationTask.java   |  9 ++-
 .../catalina/tribes/transport/bio/BioSender.java   | 26 +++----
 java/org/apache/tomcat/util/buf/ByteChunk.java     | 23 ++++--
 java/org/apache/tomcat/util/buf/MessageBytes.java  | 84 ++++++++++++++--------
 8 files changed, 106 insertions(+), 75 deletions(-)

diff --git a/java/org/apache/catalina/ha/backend/Sender.java b/java/org/apache/catalina/ha/backend/Sender.java
index 191a19c..8857445 100644
--- a/java/org/apache/catalina/ha/backend/Sender.java
+++ b/java/org/apache/catalina/ha/backend/Sender.java
@@ -18,19 +18,23 @@
 
 package org.apache.catalina.ha.backend;
 
-/*
- * Interface to send data to proxies
- *
+/**
+ * Interface to send data to proxies.
  */
 public interface Sender {
 
   /**
    * Set the configuration parameters
+   * @param config The heartbeat listener configuration
+   * @throws Exception An error occurred
    */
   public void init(HeartbeatListener config) throws Exception;
 
   /**
    * Send the message to the proxies
+   * @param mess The message that will be sent
+   * @return <code>0</code> if no error occurred, <code>-1</code> otherwise
+   * @throws Exception An error occurred
    */
   public int send(String mess) throws Exception;
 }
diff --git a/java/org/apache/catalina/ha/backend/TcpSender.java b/java/org/apache/catalina/ha/backend/TcpSender.java
index 6c78e10..7dd9338 100644
--- a/java/org/apache/catalina/ha/backend/TcpSender.java
+++ b/java/org/apache/catalina/ha/backend/TcpSender.java
@@ -180,6 +180,7 @@ public class TcpSender
 
     /**
      * Close connection.
+     * @param i The index of the connection that will be closed
      */
     protected void close(int i) {
         try {
diff --git a/java/org/apache/catalina/tribes/ChannelReceiver.java b/java/org/apache/catalina/tribes/ChannelReceiver.java
index e90c3e2..b1dae2e 100644
--- a/java/org/apache/catalina/tribes/ChannelReceiver.java
+++ b/java/org/apache/catalina/tribes/ChannelReceiver.java
@@ -23,14 +23,13 @@ package org.apache.catalina.tribes;
  * The <code>ChannelReceiver</code> interface is the data receiver component
  * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel} interface).
  * This class may optionally implement a thread pool for parallel processing of incoming messages.
- * @author Filip Hanik
  */
 public interface ChannelReceiver extends Heartbeat {
     public static final int MAX_UDP_SIZE = 65535;
 
     /**
      * Start listening for incoming messages on the host/port
-     * @throws java.io.IOException
+     * @throws java.io.IOException Listen failed
      */
     public void start() throws java.io.IOException;
 
diff --git a/java/org/apache/catalina/tribes/transport/ReceiverBase.java b/java/org/apache/catalina/tribes/transport/ReceiverBase.java
index 380807c..43d99d5 100644
--- a/java/org/apache/catalina/tribes/transport/ReceiverBase.java
+++ b/java/org/apache/catalina/tribes/transport/ReceiverBase.java
@@ -36,16 +36,6 @@ import org.apache.catalina.tribes.util.ExecutorFactory;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 
-/**
- * <p>Title: </p>
- *
- * <p>Description: </p>
- *
- * <p>Company: </p>
- *
- * @author not attributable
- * @version 1.0
- */
 public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, RxTaskPool.TaskCreator {
 
     public static final int OPTION_DIRECT_BUFFER = 0x0004;
@@ -224,7 +214,7 @@ public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, R
      * @param portstart     Starting port for bind attempts
      * @param retries       Number of times to attempt to bind (port incremented
      *                      between attempts)
-     * @throws IOException
+     * @throws IOException Socket bind error
      */
     protected void bind(ServerSocket socket, int portstart, int retries) throws IOException {
         synchronized (bindLock) {
@@ -252,11 +242,12 @@ public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, R
 
     /**
      * Same as bind() except it does it for the UDP port
-     * @param socket
-     * @param portstart
-     * @param retries
-     * @return int
-     * @throws IOException
+     * @param socket    The socket to bind
+     * @param portstart Starting port for bind attempts
+     * @param retries   Number of times to attempt to bind (port incremented
+     *                  between attempts)
+     * @return int The retry count
+     * @throws IOException Socket bind error
      */
     protected int bindUdp(DatagramSocket socket, int portstart, int retries) throws IOException {
         InetSocketAddress addr = null;
@@ -619,4 +610,4 @@ public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, R
         this.maxIdleTime = maxIdleTime;
     }
 
-}
\ No newline at end of file
+}
diff --git a/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java b/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java
index a5d724f..88546dc 100644
--- a/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java
+++ b/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java
@@ -40,8 +40,6 @@ import org.apache.juli.logging.LogFactory;
  * serviceChannel() method stores the key reference in the thread object then
  * calls notify() to wake it up. When the channel has been drained, the worker
  * thread returns itself to its parent pool.
- *
- * @author Filip Hanik
  */
 public class BioReplicationTask extends AbstractRxTask {
 
@@ -134,8 +132,9 @@ public class BioReplicationTask extends AbstractRxTask {
      * interest in OP_READ.  When this method completes it
      * re-enables OP_READ and calls wakeup() on the selector
      * so the selector will resume watching this channel.
+     * @throws Exception IO exception or execute exception
      */
-    protected void drainSocket () throws Exception {
+    protected void drainSocket() throws Exception {
         InputStream in = socket.getInputStream();
         // loop while data available, channel is non-blocking
         byte[] buf = new byte[1024];
@@ -149,8 +148,8 @@ public class BioReplicationTask extends AbstractRxTask {
 
 
     /**
-     * send a reply-acknowledgment (6,2,3)
-     * @param command
+     * Send a reply-acknowledgment (6,2,3)
+     * @param command The command to write
      */
     protected void sendAck(byte[] command) {
         try {
diff --git a/java/org/apache/catalina/tribes/transport/bio/BioSender.java b/java/org/apache/catalina/tribes/transport/bio/BioSender.java
index cf9c73e..e95a8df 100644
--- a/java/org/apache/catalina/tribes/transport/bio/BioSender.java
+++ b/java/org/apache/catalina/tribes/transport/bio/BioSender.java
@@ -30,23 +30,24 @@ import org.apache.catalina.tribes.transport.AbstractSender;
 import org.apache.catalina.tribes.transport.Constants;
 import org.apache.catalina.tribes.transport.SenderState;
 import org.apache.catalina.tribes.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
 
 /**
  * Send cluster messages with only one socket. Ack and keep Alive Handling is
  * supported
  *
  * @author Peter Rossbach
- * @author Filip Hanik
  * @since 5.5.16
  */
 public class BioSender extends AbstractSender {
 
-    private static final org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(BioSender.class);
+    private static final Log log = LogFactory.getLog(BioSender.class);
 
     /**
      * The string manager for this package.
      */
-    protected static final StringManager sm = StringManager.getManager(Constants.Package);
+    protected static final StringManager sm = StringManager.getManager(BioSender.class);
 
     // ----------------------------------------------------- Instance Variables
 
@@ -76,7 +77,7 @@ public class BioSender extends AbstractSender {
     // ------------------------------------------------------------- Properties
 
     /**
-     * Return descriptive information about this implementation and the
+     * @return descriptive information about this implementation and the
      * corresponding version number, in the format
      * <code>&lt;description&gt;/&lt;version&gt;</code>.
      */
@@ -147,9 +148,6 @@ public class BioSender extends AbstractSender {
     }
 
 
-    /**
-     * Name of this SockerSender
-     */
     @Override
     public String toString() {
         StringBuilder buf = new StringBuilder("DataSender[(");
@@ -161,8 +159,9 @@ public class BioSender extends AbstractSender {
     // --------------------------------------------------------- Protected Methods
 
     /**
-     * open real socket and set time out when waitForAck is enabled
-     * is socket open return directly
+     * Open real socket and set time out when waitForAck is enabled
+     * is socket open return directly.
+     * @throws IOException Error opening socket
      */
     protected void openSocket() throws IOException {
        if(isConnected()) return ;
@@ -233,8 +232,10 @@ public class BioSender extends AbstractSender {
      * @see #openSocket()
      * @see #sendMessage(byte[], boolean)
      *
-     * @param data
-     *            data to send
+     * @param data Data to send
+     * @param reconnect Do a reconnect (close socket then reopen)
+     * @param waitForAck Wait for an acknowledgement
+     * @throws IOException IO error writing data
      * @since 5.5.10
      */
 
@@ -252,8 +253,7 @@ public class BioSender extends AbstractSender {
     /**
      * Wait for Acknowledgement from other server.
      * FIXME Please, not wait only for three characters, better control that the wait ack message is correct.
-     * @throws java.io.IOException
-     * @throws java.net.SocketTimeoutException
+     * @throws IOException An IO error occurred
      */
     protected void waitForAck() throws java.io.IOException {
         try {
diff --git a/java/org/apache/tomcat/util/buf/ByteChunk.java b/java/org/apache/tomcat/util/buf/ByteChunk.java
index f80d801..b553c6a 100644
--- a/java/org/apache/tomcat/util/buf/ByteChunk.java
+++ b/java/org/apache/tomcat/util/buf/ByteChunk.java
@@ -78,6 +78,9 @@ public final class ByteChunk extends AbstractChunk {
         /**
          * Read new bytes.
          *
+         * @param cbuf bytes that will be read
+         * @param off offset in the bytes array
+         * @param len length that will be read
          * @return The number of bytes read
          *
          * @throws IOException If an I/O error occurs during reading
@@ -140,6 +143,7 @@ public final class ByteChunk extends AbstractChunk {
 
 
     /**
+     * @return {@link #clone()}
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
     @Deprecated
@@ -213,6 +217,7 @@ public final class ByteChunk extends AbstractChunk {
 
 
     /**
+     * @param optimizedWrite The new setting
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
     @Deprecated
@@ -277,8 +282,8 @@ public final class ByteChunk extends AbstractChunk {
     /**
      * Append a char, by casting it to byte. This IS NOT intended for unicode.
      *
-     * @param c
-     * @throws IOException
+     * @param c The character to append
+     * @throws IOException If the buffer is flushed and an I/O error occurs
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
     @Deprecated
@@ -369,6 +374,10 @@ public final class ByteChunk extends AbstractChunk {
 
 
     /**
+     * @param src To fill
+     * @return Count of bytes transferred
+     * @throws IOException If an I/O occurs obtaining more data to fill the
+     *                     supplied ByteChunk
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
     @Deprecated
@@ -522,6 +531,7 @@ public final class ByteChunk extends AbstractChunk {
 
 
     /**
+     * @return The content parsed as an int
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
     @Deprecated
@@ -650,10 +660,9 @@ public final class ByteChunk extends AbstractChunk {
 
 
     /**
-     * Returns true if the buffer starts with the specified string when tested
-     * in a case sensitive manner.
-     *
      * @param s the string
+     * @return true if the buffer starts with the specified string when tested
+     * in a case sensitive manner.
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
     @Deprecated
@@ -675,7 +684,8 @@ public final class ByteChunk extends AbstractChunk {
 
 
     /**
-     * Returns true if the message bytes start with the specified byte array.
+     * @param b2 The byte array to look for
+     * @return true if the message bytes start with the specified byte array.
      *
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
@@ -755,6 +765,7 @@ public final class ByteChunk extends AbstractChunk {
 
 
     /**
+     * @return Hash for lower case version of contents
      * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
      */
     @Deprecated
diff --git a/java/org/apache/tomcat/util/buf/MessageBytes.java b/java/org/apache/tomcat/util/buf/MessageBytes.java
index b8dbf43..a83f83c 100644
--- a/java/org/apache/tomcat/util/buf/MessageBytes.java
+++ b/java/org/apache/tomcat/util/buf/MessageBytes.java
@@ -52,7 +52,7 @@ public final class MessageBytes implements Cloneable, Serializable {
     public static final int T_CHARS = 3;
 
     private int hashCode=0;
-    // did we computed the hashcode ?
+    // did we compute the hashcode ?
     private boolean hasHashCode=false;
 
     // Internal objects to represent array + offset, and specific methods
@@ -73,7 +73,9 @@ public final class MessageBytes implements Cloneable, Serializable {
     private MessageBytes() {
     }
 
-    /** Construct a new MessageBytes instance
+    /**
+     * Construct a new MessageBytes instance.
+     * @return the instance
      */
     public static MessageBytes newInstance() {
         return factory.newInstance();
@@ -133,9 +135,9 @@ public final class MessageBytes implements Cloneable, Serializable {
     /**
      * Sets the content to be a char[]
      *
-     * @param c the bytes
-     * @param off the start offset of the bytes
-     * @param len the length of the bytes
+     * @param c the chars
+     * @param off the start offset of the chars
+     * @param len the length of the chars
      */
     public void setChars( char[] c, int off, int len ) {
         charC.setChars( c, off, len );
@@ -148,6 +150,7 @@ public final class MessageBytes implements Cloneable, Serializable {
 
     /**
      * Set the content to be a string
+     * @param s The string
      */
     public void setString( String s ) {
         strValue=s;
@@ -165,30 +168,34 @@ public final class MessageBytes implements Cloneable, Serializable {
 
     // -------------------- Conversion and getters --------------------
 
-    /** Compute the string value
+    /**
+     * Compute the string value.
+     * @return the string
      */
     @Override
     public String toString() {
-        if( hasStrValue ) {
+        if (hasStrValue) {
             return strValue;
         }
 
         switch (type) {
         case T_CHARS:
-            strValue=charC.toString();
-            hasStrValue=true;
+            strValue = charC.toString();
+            hasStrValue = true;
             return strValue;
         case T_BYTES:
-            strValue=byteC.toString();
-            hasStrValue=true;
+            strValue = byteC.toString();
+            hasStrValue = true;
             return strValue;
         }
         return null;
     }
 
     //----------------------------------------
-    /** Return the type of the original content. Can be
-     *  T_STR, T_BYTES, T_CHARS or T_NULL
+    /**
+     * Return the type of the original content. Can be
+     * T_STR, T_BYTES, T_CHARS or T_NULL
+     * @return the type
      */
     public int getType() {
         return type;
@@ -197,6 +204,7 @@ public final class MessageBytes implements Cloneable, Serializable {
     /**
      * Returns the byte chunk, representing the byte[] and offset/length.
      * Valid only if T_BYTES or after a conversion was made.
+     * @return the byte chunk
      */
     public ByteChunk getByteChunk() {
         return byteC;
@@ -205,6 +213,7 @@ public final class MessageBytes implements Cloneable, Serializable {
     /**
      * Returns the char chunk, representing the char[] and offset/length.
      * Valid only if T_CHARS or after a conversion was made.
+     * @return the char chunk
      */
     public CharChunk getCharChunk() {
         return charC;
@@ -213,13 +222,14 @@ public final class MessageBytes implements Cloneable, Serializable {
     /**
      * Returns the string value.
      * Valid only if T_STR or after a conversion was made.
+     * @return the string
      */
     public String getString() {
         return strValue;
     }
 
     /**
-     * Get the Charset used for string&lt;-&gt;byte conversions.
+     * @return the Charset used for string&lt;-&gt;byte conversions.
      */
     public Charset getCharset() {
         return byteC.getCharset();
@@ -227,37 +237,48 @@ public final class MessageBytes implements Cloneable, Serializable {
 
     /**
      * Set the Charset used for string&lt;-&gt;byte conversions.
+     * @param charset The charset
      */
     public void setCharset(Charset charset) {
         byteC.setCharset(charset);
     }
 
-    /** Do a char-&gt;byte conversion.
+
+    /**
+     * Do a char-&gt;byte conversion.
      */
     public void toBytes() {
+        if (isNull()) {
+            return;
+        }
         if (!byteC.isNull()) {
-            type=T_BYTES;
+            type = T_BYTES;
             return;
         }
         toString();
-        type=T_BYTES;
+        type = T_BYTES;
         Charset charset = byteC.getCharset();
         ByteBuffer result = charset.encode(strValue);
         byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
     }
 
-    /** Convert to char[] and fill the CharChunk.
-     *  XXX Not optimized - it converts to String first.
+
+    /**
+     * Convert to char[] and fill the CharChunk.
+     * XXX Not optimized - it converts to String first.
      */
     public void toChars() {
-        if( ! charC.isNull() ) {
-            type=T_CHARS;
+        if (isNull()) {
+            return;
+        }
+        if (!charC.isNull()) {
+            type = T_CHARS;
             return;
         }
         // inefficient
         toString();
-        type=T_CHARS;
-        char cc[]=strValue.toCharArray();
+        type = T_CHARS;
+        char cc[] = strValue.toCharArray();
         charC.setChars(cc, 0, cc.length);
     }
 
@@ -266,6 +287,7 @@ public final class MessageBytes implements Cloneable, Serializable {
      * Returns the length of the original buffer.
      * Note that the length in bytes may be different from the length
      * in chars.
+     * @return the length
      */
     public int getLength() {
         if(type==T_BYTES) {
@@ -289,7 +311,7 @@ public final class MessageBytes implements Cloneable, Serializable {
     /**
      * Compares the message bytes to the specified String object.
      * @param s the String to compare
-     * @return true if the comparison succeeded, false otherwise
+     * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
      */
     public boolean equals(String s) {
         switch (type) {
@@ -310,7 +332,7 @@ public final class MessageBytes implements Cloneable, Serializable {
     /**
      * Compares the message bytes to the specified String object.
      * @param s the String to compare
-     * @return true if the comparison succeeded, false otherwise
+     * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
      */
     public boolean equalsIgnoreCase(String s) {
         switch (type) {
@@ -389,7 +411,7 @@ public final class MessageBytes implements Cloneable, Serializable {
     }
 
     /**
-     * Returns true if the message bytes starts with the specified string.
+     * @return <code>true</code> if the message bytes starts with the specified string.
      * @param s the string
      * @param pos The start position
      */
@@ -501,8 +523,10 @@ public final class MessageBytes implements Cloneable, Serializable {
         }
     }
 
-    /** Copy the src into this MessageBytes, allocating more space if
-     *  needed
+    /**
+     * Copy the src into this MessageBytes, allocating more space if needed.
+     * @param src The source
+     * @throws IOException Writing overflow data to the output channel failed
      */
     public void duplicate( MessageBytes src ) throws IOException
     {
@@ -648,7 +672,9 @@ public final class MessageBytes implements Cloneable, Serializable {
     }
 
     // Used for headers conversion
-    /** Convert the buffer to an long, cache the value
+    /**
+     * Convert the buffer to an long, cache the value.
+     * @return the long value
      */
     public long getLong() {
         if( hasLongValue ) {


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 01/02: Align with 8.5.x/9.0.x

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 661f583a14dd509e39d084dc50c7c94ffc7993fd
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Nov 8 22:10:46 2019 +0000

    Align with 8.5.x/9.0.x
---
 .../apache/catalina/tribes/util/StringManager.java | 177 +++++++++++++++++----
 1 file changed, 145 insertions(+), 32 deletions(-)

diff --git a/java/org/apache/catalina/tribes/util/StringManager.java b/java/org/apache/catalina/tribes/util/StringManager.java
index 2204245..53caf03 100644
--- a/java/org/apache/catalina/tribes/util/StringManager.java
+++ b/java/org/apache/catalina/tribes/util/StringManager.java
@@ -18,11 +18,15 @@
 package org.apache.catalina.tribes.util;
 
 import java.text.MessageFormat;
+import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.LinkedHashMap;
 import java.util.Locale;
+import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
+
 /**
  * An internationalization / localization helper class which reduces
  * the bother of handling ResourceBundles and takes care of the
@@ -49,11 +53,14 @@ import java.util.ResourceBundle;
  */
 public class StringManager {
 
+    private static int LOCALE_CACHE_SIZE = 10;
+
     /**
      * The ResourceBundle for this StringManager.
      */
-    private ResourceBundle bundle;
-    private Locale locale;
+    private final ResourceBundle bundle;
+    private final Locale locale;
+
 
     /**
      * Creates a new StringManager for a given package. This is a
@@ -63,53 +70,67 @@ public class StringManager {
      *
      * @param packageName Name of package to create StringManager for.
      */
-    private StringManager(String packageName) {
+    private StringManager(String packageName, Locale locale) {
         String bundleName = packageName + ".LocalStrings";
+        ResourceBundle bnd = null;
         try {
-            bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
-        } catch( MissingResourceException ex ) {
+            bnd = ResourceBundle.getBundle(bundleName, locale);
+        } catch (MissingResourceException ex) {
             // Try from the current loader (that's the case for trusted apps)
             // Should only be required if using a TC5 style classloader structure
             // where common != shared != server
             ClassLoader cl = Thread.currentThread().getContextClassLoader();
-            if( cl != null ) {
+            if (cl != null) {
                 try {
-                    bundle = ResourceBundle.getBundle(
-                            bundleName, Locale.getDefault(), cl);
-                } catch(MissingResourceException ex2) {
+                    bnd = ResourceBundle.getBundle(bundleName, locale, cl);
+                } catch (MissingResourceException ex2) {
                     // Ignore
                 }
             }
         }
+        bundle = bnd;
         // Get the actual locale, which may be different from the requested one
         if (bundle != null) {
-            locale = bundle.getLocale();
+            Locale bundleLocale = bundle.getLocale();
+            if (bundleLocale.equals(Locale.ROOT)) {
+                this.locale = Locale.ENGLISH;
+            } else {
+                this.locale = bundleLocale;
+            }
+        } else {
+            this.locale = null;
         }
     }
 
-    /**
-        Get a string from the underlying resource bundle or return
-        null if the String is not found.
 
-        @param key to desired resource String
-        @return resource String matching <i>key</i> from underlying
-                bundle or null if not found.
-        @throws IllegalArgumentException if <i>key</i> is null.
+    /**
+     * Get a string from the underlying resource bundle or return null if the
+     * String is not found.
+     *
+     * @param key to desired resource String
+     *
+     * @return resource String matching <i>key</i> from underlying bundle or
+     *         null if not found.
+     *
+     * @throws IllegalArgumentException if <i>key</i> is null
      */
     public String getString(String key) {
-        if(key == null){
+        if (key == null){
             String msg = "key may not have a null value";
-
             throw new IllegalArgumentException(msg);
         }
 
         String str = null;
 
         try {
-            str = bundle.getString(key);
-        } catch(MissingResourceException mre) {
+            // Avoid NPE if bundle is null and treat it like an MRE
+            if (bundle != null) {
+                str = bundle.getString(key);
+            }
+        } catch (MissingResourceException mre) {
             //bad: shouldn't mask an exception the following way:
-            //   str = "[cannot find message associated with key '" + key + "' due to " + mre + "]";
+            //   str = "[cannot find message associated with key '" + key +
+            //         "' due to " + mre + "]";
             //     because it hides the fact that the String was missing
             //     from the calling code.
             //good: could just throw the exception (or wrap it in another)
@@ -124,12 +145,15 @@ public class StringManager {
         return str;
     }
 
+
     /**
      * Get a string from the underlying resource bundle and format
      * it with the given set of arguments.
      *
-     * @param key
-     * @param args
+     * @param key  The key for the required message
+     * @param args The values to insert into the message
+     *
+     * @return The requested string formatted with the provided arguments
      */
     public String getString(final String key, final Object... args) {
         String value = getString(key);
@@ -142,27 +166,116 @@ public class StringManager {
         return mf.format(args, new StringBuffer(), null).toString();
     }
 
+
+    /**
+     * Identify the Locale this StringManager is associated with
+     *
+     * @return The Locale associated with this instance
+     */
+    public Locale getLocale() {
+        return locale;
+    }
+
+
     // --------------------------------------------------------------
     // STATIC SUPPORT METHODS
     // --------------------------------------------------------------
 
-    private static Hashtable<String, StringManager> managers =
-        new Hashtable<String, StringManager>();
+    private static final Map<String, Map<Locale,StringManager>> managers =
+            new Hashtable<String, Map<Locale, StringManager>>();
+
 
     /**
-     * Get the StringManager for a particular package. If a manager for
-     * a package already exists, it will be reused, else a new
+     * The StringManager will be returned for the package in which the class is
+     * located. If a manager for that package already exists, it will be reused,
+     * else a new StringManager will be created and returned.
+     *
+     * @param clazz The class for which to retrieve the StringManager
+     *
+     * @return The StringManager for the given class.
+     */
+    public static final StringManager getManager(Class<?> clazz) {
+        return getManager(clazz.getPackage().getName());
+    }
+
+
+    /**
+     * If a manager for a package already exists, it will be reused, else a new
      * StringManager will be created and returned.
      *
      * @param packageName The package name
+     *
+     * @return The StringManager for the given package.
+     */
+    public static final StringManager getManager(String packageName) {
+        return getManager(packageName, Locale.getDefault());
+    }
+
+
+    /**
+     * If a manager for a package/Locale combination already exists, it will be
+     * reused, else a new StringManager will be created and returned.
+     *
+     * @param packageName The package name
+     * @param locale      The Locale
+     *
+     * @return The StringManager for a particular package and Locale
      */
-    public static final synchronized StringManager getManager(String packageName) {
-        StringManager mgr = managers.get(packageName);
+    public static final synchronized StringManager getManager(
+            String packageName, Locale locale) {
+
+        Map<Locale,StringManager> map = managers.get(packageName);
+        if (map == null) {
+            /*
+             * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE.
+             * Expansion occurs when size() exceeds capacity. Therefore keep
+             * size at or below capacity.
+             * removeEldestEntry() executes after insertion therefore the test
+             * for removal needs to use one less than the maximum desired size
+             *
+             */
+            map = new LinkedHashMap<Locale,StringManager>(LOCALE_CACHE_SIZE, 1, true) {
+                private static final long serialVersionUID = 1L;
+                @Override
+                protected boolean removeEldestEntry(
+                        Map.Entry<Locale,StringManager> eldest) {
+                    if (size() > (LOCALE_CACHE_SIZE - 1)) {
+                        return true;
+                    }
+                    return false;
+                }
+            };
+            managers.put(packageName, map);
+        }
+
+        StringManager mgr = map.get(locale);
         if (mgr == null) {
-            mgr = new StringManager(packageName);
-            managers.put(packageName, mgr);
+            mgr = new StringManager(packageName, locale);
+            map.put(locale, mgr);
         }
         return mgr;
     }
 
+
+    /**
+     * Retrieve the StringManager for a list of Locales. The first StringManager
+     * found will be returned.
+     *
+     * @param packageName The package for which the StringManager is required
+     * @param requestedLocales the list of Locales
+     *
+     * @return the found StringManager or the default StringManager
+     */
+    public static StringManager getManager(String packageName,
+            Enumeration<Locale> requestedLocales) {
+        while (requestedLocales.hasMoreElements()) {
+            Locale locale = requestedLocales.nextElement();
+            StringManager result = getManager(packageName, locale);
+            if (result.getLocale().equals(locale)) {
+                return result;
+            }
+        }
+        // Return the default
+        return getManager(packageName);
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org