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

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

commit 318dd70e43874299335b67fb18ca12106f3affd3
Author: opencode <[email protected]>
AuthorDate: Sat Oct 31 15:34:28 2026 +0100

    Localize the pool's exception and log messages
    
    The pool carried its diagnostics as string literals at each throw and
    log call, leaving its reporting path untranslatable while every other
    Tomcat component reads its messages from a LocalStrings bundle.
    
    Add org.apache.tomcat.jdbc.pool.util.MessageFactory, modeled on
    org.apache.el.util.MessageFactory, and route every exception message
    and every info, warn and error log message through it. Debug and trace
    messages stay literals, as elsewhere in Tomcat, and a message that
    carries nothing but a passed-through exception's own text has nothing
    to localize. The keys follow the main tree's convention of
    lowercaseClassName.camelCaseKey, and the default bundle keeps the
    English text exactly as it was: pool code scans the borrow-timeout
    message for NoWait, and the tests assert the slow-query report's
    format, so the wording is load-bearing. Those tests pass unchanged.
    
    The bundle sits with the sources, like the LocalStrings files
    elsewhere in the tree. The Maven build gains a resources section so
    the bundle reaches the test class path and the jars, and the module's
    Ant build copies it into the classes directory so the built
    tomcat-jdbc.jar ships it too. One blank line goes with them from a
    test's javadoc, where the checkstyle run this change passes required
    the comment's content to start beside the opening delimiter.
---
 modules/jdbc-pool/build.xml                        |   7 +
 modules/jdbc-pool/pom.xml                          |  11 ++
 .../jdbc/naming/GenericNamingResourcesFactory.java |  36 ++--
 .../apache/tomcat/jdbc/pool/ClassLoaderUtil.java   |   6 +-
 .../apache/tomcat/jdbc/pool/ConnectionPool.java    | 117 ++++++------
 .../org/apache/tomcat/jdbc/pool/DataSource.java    |   8 +-
 .../apache/tomcat/jdbc/pool/DataSourceFactory.java |  35 ++--
 .../apache/tomcat/jdbc/pool/DataSourceProxy.java   |  34 ++--
 .../jdbc/pool/DisposableConnectionFacade.java      |   4 +-
 .../apache/tomcat/jdbc/pool/FairBlockingQueue.java |  31 ++--
 .../apache/tomcat/jdbc/pool/PoolProperties.java    |  17 +-
 .../apache/tomcat/jdbc/pool/PooledConnection.java  |  52 +++---
 .../apache/tomcat/jdbc/pool/ProxyConnection.java   |  17 +-
 .../apache/tomcat/jdbc/pool/StatementFacade.java   |  11 +-
 .../org/apache/tomcat/jdbc/pool/TrapException.java |   8 +-
 .../org/apache/tomcat/jdbc/pool/XADataSource.java  |  18 +-
 .../AbstractCreateStatementInterceptor.java        |  12 +-
 .../jdbc/pool/interceptor/AbstractQueryReport.java |  17 +-
 .../jdbc/pool/interceptor/ConnectionState.java     |  23 ++-
 .../pool/interceptor/QueryTimeoutInterceptor.java  |  13 +-
 .../jdbc/pool/interceptor/ResetAbandonedTimer.java |  12 +-
 .../jdbc/pool/interceptor/SlowQueryReport.java     |  11 +-
 .../jdbc/pool/interceptor/SlowQueryReportJmx.java  |  21 +--
 .../jdbc/pool/interceptor/StatementCache.java      |   7 +-
 .../interceptor/StatementDecoratorInterceptor.java |   3 +-
 .../jdbc/pool/interceptor/StatementFinalizer.java  |   5 +-
 .../tomcat/jdbc/pool/jmx/ConnectionPool.java       |   5 +-
 .../org/apache/tomcat/jdbc/pool/jmx/JmxUtil.java   |   5 +-
 .../tomcat/jdbc/pool/util/LocalStrings.properties  | 199 +++++++++++++++++++++
 .../tomcat/jdbc/pool/util/MessageFactory.java      | 116 ++++++++++++
 .../interceptor/TestSlowQueryReportLogging.java    |   1 -
 31 files changed, 591 insertions(+), 271 deletions(-)

diff --git a/modules/jdbc-pool/build.xml b/modules/jdbc-pool/build.xml
index 01d7340fd8..7225c58698 100644
--- a/modules/jdbc-pool/build.xml
+++ b/modules/jdbc-pool/build.xml
@@ -154,6 +154,13 @@
       <include name="org/apache/tomcat/jdbc/**" />
     </javac>
 
+    <!-- Localized messages are stored with the sources -->
+    <copy todir="${tomcat.classes}">
+      <fileset dir="${basedir}/src/main/java">
+        <include name="org/apache/tomcat/jdbc/**/*.properties" />
+      </fileset>
+    </copy>
+
     <!-- connection pool JAR File -->
     <jar jarfile="${tomcat-jdbc.jar}"
          update="true"
diff --git a/modules/jdbc-pool/pom.xml b/modules/jdbc-pool/pom.xml
index 1cfdc6e833..890a7a645b 100644
--- a/modules/jdbc-pool/pom.xml
+++ b/modules/jdbc-pool/pom.xml
@@ -81,6 +81,17 @@
     </dependency>
   </dependencies>
   <build>
+    <resources>
+      <!-- Localized messages are stored with the sources. Copy them, like
+           the Ant build, to make them available on the runtime and test
+           class paths. -->
+      <resource>
+        <directory>src/main/java</directory>
+        <includes>
+          <include>**/*.properties</include>
+        </includes>
+      </resource>
+    </resources>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
index 685795d017..ec1dde2d3a 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
@@ -37,6 +37,7 @@ import javax.naming.spi.ObjectFactory;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.ClassLoaderUtil;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Simple way of configuring generic resources by using reflection. Example 
usage:
@@ -82,8 +83,8 @@ public class GenericNamingResourcesFactory implements 
ObjectFactory {
 
         String type = ref.getClassName();
         if (type == null || type.isEmpty()) {
-            throw new NamingException("No resource type (the \"type\" 
attribute) is defined for reference [" + name +
-                    "]; GenericNamingResourcesFactory needs the class of the " 
+ "object to create.");
+            throw new NamingException(
+                    
MessageFactory.get("genericNamingResourcesFactory.noResourceType", name));
         }
         Object o;
         try {
@@ -91,7 +92,8 @@ public class GenericNamingResourcesFactory implements 
ObjectFactory {
                     Thread.currentThread().getContextClassLoader());
             o = clazz.getConstructor().newInstance();
         } catch (ClassNotFoundException e) {
-            NamingException ne = new NamingException("Class [" + type + "] of 
reference [" + name + "] was not found");
+            NamingException ne =
+                    new 
NamingException(MessageFactory.get("genericNamingResourcesFactory.classNotFound",
 type, name));
             ne.setRootCause(e);
             throw ne;
         } catch (InvocationTargetException e) {
@@ -103,16 +105,17 @@ public class GenericNamingResourcesFactory implements 
ObjectFactory {
             if (cause instanceof VirtualMachineError) {
                 throw (VirtualMachineError) cause;
             }
-            NamingException ne = new NamingException("The constructor of class 
[" + type + "] of reference [" + name +
-                    "] threw an exception and the object cannot be created");
+            NamingException ne =
+                    new 
NamingException(MessageFactory.get("genericNamingResourcesFactory.constructorThrew",
 type, name));
             ne.setRootCause(e);
             throw ne;
         } catch (ReflectiveOperationException e) {
             // No public no-arg constructor (NoSuchMethodException), or the
             // class cannot be instantiated (abstract, interface, illegal
             // access).
-            NamingException ne = new NamingException("Class [" + type + "] of 
reference [" + name +
-                    "] has no usable public no-argument constructor and " + 
"cannot be created by this factory");
+            NamingException ne =
+                    new 
NamingException(MessageFactory.get("genericNamingResourcesFactory.noUsableConstructor",
+                            type, name));
             ne.setRootCause(e);
             throw ne;
         }
@@ -129,8 +132,7 @@ public class GenericNamingResourcesFactory implements 
ObjectFactory {
                 value = addr.getContent().toString();
             }
             if (!setProperty(o, param, value)) {
-                log.warn("Property [" + param + "] was not applied to [" + 
type + "]: no usable setter found, or the " +
-                        "configured value could not be applied.");
+                
log.warn(MessageFactory.get("genericNamingResourcesFactory.propertyNotApplied", 
param, type));
             }
         }
         return o;
@@ -279,14 +281,14 @@ public class GenericNamingResourcesFactory implements 
ObjectFactory {
             }
 
         } catch (IllegalArgumentException ex2) {
-            log.warn("Unable to set property [" + name + "] with value [" + 
value + "] on [" + o.getClass().getName() +
-                    "]", ex2);
+            
log.warn(MessageFactory.get("genericNamingResourcesFactory.unableToSetProperty",
+                    name, value, o.getClass().getName()), ex2);
         } catch (SecurityException ex1) {
-            log.warn("SecurityException setting property [" + name + "] with 
value [" + value + "] on [" +
-                    o.getClass().getName() + "]", ex1);
+            
log.warn(MessageFactory.get("genericNamingResourcesFactory.securityExceptionSettingProperty",
+                    name, value, o.getClass().getName()), ex1);
         } catch (IllegalAccessException iae) {
-            log.warn("IllegalAccessException setting property [" + name + "] 
with value [" + value + "] on [" +
-                    o.getClass().getName() + "]", iae);
+            
log.warn(MessageFactory.get("genericNamingResourcesFactory.illegalAccessExceptionSettingProperty",
+                    name, value, o.getClass().getName()), iae);
         } catch (InvocationTargetException ie) {
             // In the spirit of ExceptionUtils.handleThrowable(), which is not
             // available to tomcat-jdbc.jar: propagate the fatal
@@ -298,8 +300,8 @@ public class GenericNamingResourcesFactory implements 
ObjectFactory {
             if (cause instanceof VirtualMachineError) {
                 throw (VirtualMachineError) cause;
             }
-            log.warn("Setter for property [" + name + "] with value [" + value 
+ "] threw an exception on [" +
-                    o.getClass().getName() + "]", ie);
+            
log.warn(MessageFactory.get("genericNamingResourcesFactory.setterThrew",
+                    name, value, o.getClass().getName()), ie);
         }
         return false;
     }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java
index 92e796f150..e425b2d60a 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java
@@ -19,6 +19,7 @@ package org.apache.tomcat.jdbc.pool;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Utility class for loading classes using multiple class loaders.
@@ -56,7 +57,7 @@ public class ClassLoaderUtil {
                     }
                     return Class.forName(className, true, cl);
                 } else {
-                    throw new ClassNotFoundException("Classloader is null");
+                    throw new 
ClassNotFoundException(MessageFactory.get("classLoaderUtil.classLoaderNull"));
                 }
             } catch (ClassNotFoundException x) {
                 last = x;
@@ -72,7 +73,8 @@ public class ClassLoaderUtil {
                 break;
             }
         }
-        throw new ClassNotFoundException("Unable to load class: " + className 
+ " from " + errorMsg, last);
+        throw new 
ClassNotFoundException(MessageFactory.get("classLoaderUtil.classLoadFailed", 
className, errorMsg),
+                last);
     }
 
 
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
index 1d83728282..3298aefeaa 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
@@ -52,6 +52,7 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor;
 import org.apache.tomcat.jdbc.pool.interceptor.StatementCache;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Implementation of simple connection pool. The ConnectionPool uses a {@link 
PoolProperties} object for storing all the
@@ -213,12 +214,11 @@ public class ConnectionPool {
                 // future will never hand out a connection. Reject the borrow 
the
                 // same way a synchronous borrow on a closed pool does, rather 
than
                 // handing out a future that only ever fails with a 
cancellation.
-                throw new SQLException("Connection pool closed.");
+                throw new 
SQLException(MessageFactory.get("connectionPool.poolClosed"));
             }
             return new ConnectionFuture(pcf);
         } else {
-            throw new SQLException(
-                    "Connection pool is misconfigured, doesn't support async 
retrieval. Set the 'fair' property to 'true'");
+            throw new 
SQLException(MessageFactory.get("connectionPool.poolMisconfigured"));
         }
     }
 
@@ -367,7 +367,7 @@ public class ConnectionPool {
             try {
                 con.createMBean();
             } catch (Exception x) {
-                SQLException sx = new SQLException("Unable to register the 
pooled connection MBean.");
+                SQLException sx = new 
SQLException(MessageFactory.get("connectionPool.unableToRegisterMBean"));
                 sx.initCause(x);
                 // Nothing has been done to the connection, it can go back to 
the pool
                 abandonSetup(con, sx, false);
@@ -388,7 +388,7 @@ public class ConnectionPool {
                     // Configure the last one to be held by the connection
                     handler = interceptor;
                 } catch (Exception x) {
-                    SQLException sx = new SQLException("Unable to instantiate 
interceptor chain.");
+                    SQLException sx = new 
SQLException(MessageFactory.get("connectionPool.unableToInstantiateInterceptorChain"));
                     sx.initCause(x);
                     // Nothing has been done to the connection, it can go back 
to the pool
                     abandonSetup(con, sx, false);
@@ -406,8 +406,8 @@ public class ConnectionPool {
                 next = next.getNext();
             }
         } catch (Exception x) {
-            SQLException sx = new SQLException(
-                    "Unable to reset interceptor [" + (next != null ? 
next.getClass().getName() : "unknown") + "].");
+            SQLException sx = new 
SQLException(MessageFactory.get("connectionPool.unableToResetInterceptor",
+                    (next != null ? next.getClass().getName() : "unknown")));
             sx.initCause(x);
             // The state of the connection is unknown, discard it so its 
partially reset chain is dropped
             abandonSetup(con, sx, true);
@@ -502,10 +502,10 @@ public class ConnectionPool {
         try {
             hint.invoke(connection);
         } catch (InvocationTargetException x) {
-            log.warn("The request boundary hint [" + hint.getName() + "] was 
refused by the driver",
+            log.warn(MessageFactory.get("connectionPool.requestHintRefused", 
hint.getName()),
                     x.getTargetException());
         } catch (ReflectiveOperationException | IllegalArgumentException x) {
-            log.warn("Unable to send the request boundary hint [" + 
hint.getName() + "]", x);
+            
log.warn(MessageFactory.get("connectionPool.unableToSendRequestHint", 
hint.getName()), x);
         }
     }
 
@@ -595,7 +595,7 @@ public class ConnectionPool {
                     } catch (Throwable x) {
                         // One failing connection must not leave the
                         // remaining idle and busy connections undisconnected
-                        log.warn("Unable to close pooled connection.", x);
+                        
log.warn(MessageFactory.get("connectionPool.unableToClosePooledConnection"), x);
                     }
                     if (!pool.isEmpty()) {
                         con = pool.poll(1000, TimeUnit.MILLISECONDS);
@@ -682,7 +682,7 @@ public class ConnectionPool {
                 interceptor.setProperties(proxies[i].getProperties());
                 interceptor.poolStarted(this);
             } catch (Exception x) {
-                log.error("Unable to inform interceptor of pool start.", x);
+                
log.error(MessageFactory.get("connectionPool.unableToInformInterceptorOfPoolStart"),
 x);
                 org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmx = jmxPool;
                 if (jmx != null) {
                     
jmx.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_INIT, 
getStackTrace(x));
@@ -707,7 +707,7 @@ public class ConnectionPool {
             }
 
         } catch (SQLException x) {
-            log.error("Unable to create initial connections of pool.", x);
+            
log.error(MessageFactory.get("connectionPool.unableToCreateInitialConnections"),
 x);
             if (!poolProperties.isIgnoreExceptionOnPreLoad()) {
                 org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmx = jmxPool;
                 if (jmx != null) {
@@ -765,11 +765,7 @@ public class ConnectionPool {
             }
         }
         if (firstCache >= 0 && lastTimeout > firstCache) {
-            log.warn("The [StatementCache] interceptor is configured before 
[QueryTimeoutInterceptor]. "
-                    + "A statement cache hit is answered by the cache without 
invoking the timeout interceptor, "
-                    + "and a statement recycled into the cache carries no 
timeout, so cached statements run "
-                    + "without a query timeout. List QueryTimeoutInterceptor 
before StatementCache in the "
-                    + "jdbcInterceptors configuration.");
+            
log.warn(MessageFactory.get("connectionPool.statementCacheBeforeQueryTimeout"));
         }
     }
 
@@ -782,29 +778,28 @@ public class ConnectionPool {
     public void checkPoolConfiguration(PoolConfiguration properties) {
         // Make sure the pool is properly configured
         if (properties.getMaxActive() < 1) {
-            log.warn("maxActive is smaller than 1, setting maxActive to: " + 
PoolProperties.DEFAULT_MAX_ACTIVE);
+            log.warn(MessageFactory.get("connectionPool.maxActiveTooSmall", 
PoolProperties.DEFAULT_MAX_ACTIVE));
             properties.setMaxActive(PoolProperties.DEFAULT_MAX_ACTIVE);
         }
         if (properties.getMaxActive() < properties.getInitialSize()) {
-            log.warn("initialSize is larger than maxActive, setting 
initialSize to: " + properties.getMaxActive());
+            log.warn(MessageFactory.get("connectionPool.initialSizeTooLarge", 
properties.getMaxActive()));
             properties.setInitialSize(properties.getMaxActive());
         }
         if (properties.getMinIdle() > properties.getMaxActive()) {
-            log.warn("minIdle is larger than maxActive, setting minIdle to: " 
+ properties.getMaxActive());
+            log.warn(MessageFactory.get("connectionPool.minIdleTooLarge", 
properties.getMaxActive()));
             properties.setMinIdle(properties.getMaxActive());
         }
         if (properties.getMaxIdle() > properties.getMaxActive()) {
-            log.warn("maxIdle is larger than maxActive, setting maxIdle to: " 
+ properties.getMaxActive());
+            log.warn(MessageFactory.get("connectionPool.maxIdleTooLarge", 
properties.getMaxActive()));
             properties.setMaxIdle(properties.getMaxActive());
         }
         if (properties.getMaxIdle() < properties.getMinIdle()) {
-            log.warn("maxIdle is smaller than minIdle, setting maxIdle to: " + 
properties.getMinIdle());
+            log.warn(MessageFactory.get("connectionPool.maxIdleTooSmall", 
properties.getMinIdle()));
             properties.setMaxIdle(properties.getMinIdle());
         }
         if (properties.getMaxAge() > 0 && properties.isPoolSweeperEnabled() &&
                 properties.getTimeBetweenEvictionRunsMillis() > 
properties.getMaxAge()) {
-            log.warn("timeBetweenEvictionRunsMillis is larger than maxAge, 
setting timeBetweenEvictionRunsMillis to: " +
-                    properties.getMaxAge());
+            
log.warn(MessageFactory.get("connectionPool.timeBetweenEvictionRunsTooLarge", 
properties.getMaxAge()));
             properties.setTimeBetweenEvictionRunsMillis((int) 
properties.getMaxAge());
         }
     }
@@ -853,7 +848,7 @@ public class ConnectionPool {
             con.lock();
             String trace = con.getStackTrace();
             if (getPoolProperties().isLogAbandoned()) {
-                log.warn("Connection has been abandoned " + con + ":" + trace);
+                
log.warn(MessageFactory.get("connectionPool.connectionAbandoned", con, trace));
             }
             org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmx = jmxPool;
             if (jmx != null) {
@@ -885,8 +880,8 @@ public class ConnectionPool {
             con.lock();
             String trace = con.getStackTrace();
             if (getPoolProperties().isLogAbandoned()) {
-                log.warn("Connection has been marked suspect, possibly 
abandoned " + con + "[" +
-                        (System.currentTimeMillis() - con.getTimestamp()) + " 
ms.]:" + trace);
+                log.warn(MessageFactory.get("connectionPool.connectionSuspect",
+                        con, (System.currentTimeMillis() - 
con.getTimestamp()), trace));
             }
             org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmx = jmxPool;
             if (jmx != null) {
@@ -937,8 +932,7 @@ public class ConnectionPool {
         // queue the placeholder while it is open.
         if (destroyed && !isClosed() && waitcount.get() > 0) {
             if (!idle.offer(create(true))) {
-                log.warn("Failed to add a new connection to the pool after 
releasing a connection " +
-                        "when at least one thread was waiting for a 
connection.");
+                
log.warn(MessageFactory.get("connectionPool.failedToAddNewConnection"));
             }
         }
     }
@@ -958,7 +952,7 @@ public class ConnectionPool {
     private PooledConnection borrowConnection(int wait, String username, 
String password) throws SQLException {
 
         if (isClosed()) {
-            throw new SQLException("Connection pool closed.");
+            throw new 
SQLException(MessageFactory.get("connectionPool.poolClosed"));
         }
 
         // Get the current time stamp
@@ -1026,7 +1020,7 @@ public class ConnectionPool {
                 if (getPoolProperties().getPropagateInterruptState()) {
                     Thread.currentThread().interrupt();
                 }
-                SQLException sx = new SQLException("Pool wait interrupted.");
+                SQLException sx = new 
SQLException(MessageFactory.get("connectionPool.poolWaitInterrupted"));
                 sx.initCause(ex);
                 throw sx;
             } finally {
@@ -1037,9 +1031,8 @@ public class ConnectionPool {
                 if (jmx != null) {
                     
jmx.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool 
empty - no wait.");
                 }
-                throw new PoolExhaustedException("[" + 
Thread.currentThread().getName() + "] " +
-                        "NoWait: Pool empty. Unable to fetch a connection, 
none available[" + busy.size() +
-                        " in use].");
+                throw new 
PoolExhaustedException(MessageFactory.get("connectionPool.poolEmptyNoWait",
+                        Thread.currentThread().getName(), busy.size()));
             }
             // We didn't get a connection, lets see if we timed out
             if (con == null) {
@@ -1049,10 +1042,9 @@ public class ConnectionPool {
                         
jmx.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY,
                                 "Pool empty - timeout.");
                     }
-                    throw new PoolExhaustedException("[" + 
Thread.currentThread().getName() + "] " +
-                            "Timeout: Pool empty. Unable to fetch a connection 
in " + (maxWait / 1000) +
-                            " seconds, none available[size:" + size.get() + "; 
busy:" + busy.size() + "; idle:" +
-                            idle.size() + "; lastwait:" + timetowait + "].");
+                    throw new 
PoolExhaustedException(MessageFactory.get("connectionPool.poolEmptyTimeout",
+                            Thread.currentThread().getName(), (maxWait / 
1000), size.get(), busy.size(),
+                            idle.size(), timetowait));
                 } else {
                     // No timeout, lets try again
                     continue;
@@ -1093,7 +1085,7 @@ public class ConnectionPool {
                 // being created; the shutdown drain has already passed over
                 // the queues, so neither hand the connection out nor register
                 // it in busy - the error path below disconnects it
-                throw new SQLException("Connection pool closed.");
+                throw new 
SQLException(MessageFactory.get("connectionPool.poolClosed"));
             }
             if (con.validate(PooledConnection.VALIDATE_INIT)) {
                 // No need to lock a new one, its not contented
@@ -1109,7 +1101,7 @@ public class ConnectionPool {
             } else {
                 // Validation failed, make sure we disconnect
                 // and clean up
-                throw new SQLException("Validation Query Failed, enable 
logValidationErrors for more details.");
+                throw new 
SQLException(MessageFactory.get("connectionPool.validationQueryFailed"));
             }
         } catch (Exception e) {
             error = true;
@@ -1202,7 +1194,7 @@ public class ConnectionPool {
                     return con;
                 } else {
                     // Validation failed.
-                    throw new SQLException("Failed to validate a newly 
established connection.");
+                    throw new 
SQLException(MessageFactory.get("connectionPool.failedToValidateNewConnection"));
                 }
             } catch (Exception x) {
                 release(con);
@@ -1267,7 +1259,7 @@ public class ConnectionPool {
             }
             return true;
         } catch (SQLException x) {
-            log.warn("Unable to terminate transaction, connection will be 
closed.", x);
+            
log.warn(MessageFactory.get("connectionPool.unableToTerminateTransaction"), x);
             return false;
         }
 
@@ -1293,7 +1285,7 @@ public class ConnectionPool {
                 return true;
             }
         } catch (SQLException e) {
-            log.warn("Unable to check if connection is closed", e);
+            log.warn(MessageFactory.get("connectionPool.unableToCheckClosed"), 
e);
             return true;
         }
         if (isClosed()) {
@@ -1339,7 +1331,7 @@ public class ConnectionPool {
                     return false;
                 }
             } catch (Exception e) {
-                log.error("Failed to re-connect connection [" + this + "] that 
expired because of maxAge", e);
+                
log.error(MessageFactory.get("connectionPool.failedToReconnectExpired", this), 
e);
                 return false;
             }
         }
@@ -1367,9 +1359,8 @@ public class ConnectionPool {
                 con.lock();
                 if (con.isSuspect()) {
                     if (poolProperties.isLogAbandoned() && 
log.isInfoEnabled()) {
-                        log.info("Connection(" + con + ") that has been marked 
suspect was returned." +
-                                " The processing time is " + 
(System.currentTimeMillis() - con.getTimestamp()) +
-                                " ms.");
+                        
log.info(MessageFactory.get("connectionPool.suspectConnectionReturned",
+                                con, (System.currentTimeMillis() - 
con.getTimestamp())));
                     }
                     org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmx = 
jmxPool;
                     if (jmx != null) {
@@ -1427,7 +1418,7 @@ public class ConnectionPool {
                         // the driver that failed.
                         con.setDiscarded(true);
                         release(con);
-                        log.warn("Unable to return connection [" + con + "] to 
the pool, closing it.", x);
+                        
log.warn(MessageFactory.get("connectionPool.unableToReturnConnection", con), x);
                     }
                 } else {
                     if (log.isDebugEnabled()) {
@@ -1509,7 +1500,7 @@ public class ConnectionPool {
                 log.debug("checkAbandoned failed.", e);
             }
         } catch (Exception e) {
-            log.warn("checkAbandoned failed, it will be retried.", e);
+            
log.warn(MessageFactory.get("connectionPool.checkAbandonedFailed"), e);
         }
     }
 
@@ -1571,7 +1562,7 @@ public class ConnectionPool {
                 log.debug("checkIdle failed.", e);
             }
         } catch (Exception e) {
-            log.warn("checkIdle failed, it will be retried.", e);
+            log.warn(MessageFactory.get("connectionPool.checkIdleFailed"), e);
         }
 
     }
@@ -1661,7 +1652,7 @@ public class ConnectionPool {
                         // service instead, as a failed check already does.
                         con.setDiscarded(true);
                         release(con);
-                        log.warn("Unable to test idle connection [" + con + 
"], closing it.", x);
+                        
log.warn(MessageFactory.get("connectionPool.unableToTestIdleConnection", con), 
x);
                         continue;
                     }
                     if (keep) {
@@ -1697,7 +1688,7 @@ public class ConnectionPool {
                 log.debug("testAllIdle failed.", e);
             }
         } catch (Exception e) {
-            log.warn("testAllIdle failed, it will be retried.", e);
+            log.warn(MessageFactory.get("connectionPool.testAllIdleFailed"), 
e);
         }
 
     }
@@ -1793,8 +1784,8 @@ public class ConnectionPool {
             } catch (Throwable t) {
                 // One broken interceptor must not stop the rest of the
                 // chain from being reset
-                log.warn("Interceptor [" + handler.getClass().getName() + "] 
threw an exception from reset(); " +
-                        "continuing with the rest of the chain.", t);
+                
log.warn(MessageFactory.get("connectionPool.interceptorResetFailed",
+                        handler.getClass().getName()), t);
             }
             handler = next;
         }
@@ -1817,8 +1808,8 @@ public class ConnectionPool {
             } catch (Throwable t) {
                 // One broken interceptor must not stop the rest of the
                 // chain from being notified
-                log.warn("Interceptor [" + handler.getClass().getName() + "] 
threw an exception from disconnected(); " +
-                        "continuing with the rest of the chain.", t);
+                
log.warn(MessageFactory.get("connectionPool.interceptorDisconnectedFailed",
+                        handler.getClass().getName()), t);
             }
             handler = next;
         }
@@ -1840,8 +1831,7 @@ public class ConnectionPool {
         try {
             jmxPool = new org.apache.tomcat.jdbc.pool.jmx.ConnectionPool(this);
         } catch (Exception x) {
-            log.warn("Unable to start JMX integration for connection pool. 
Instance[" + getName() +
-                    "] can't be monitored.", x);
+            log.warn(MessageFactory.get("connectionPool.unableToStartJmx", 
getName()), x);
         }
     }
 
@@ -2134,10 +2124,7 @@ public class ConnectionPool {
                     // fails the caller instead. Every caller released on the
                     // latch below reports what this publishes, and an
                     // ExecutionException with no cause tells them nothing.
-                    cause = new SQLException("The pooled connection served to" 
+
-                            " this asynchronous borrow was released before it" 
+
-                            " could be set up; no connection was borrowed," +
-                            " borrow the pool again.");
+                    cause = new 
SQLException(MessageFactory.get("connectionPool.asyncBorrowConnectionReleased"));
                 }
             } catch (SQLException x) {
                 cause = x;
@@ -2204,7 +2191,7 @@ public class ConnectionPool {
                     }
                 }
             } catch (Exception x) {
-                log.error("Unable to cancel ConnectionFuture.", x);
+                
log.error(MessageFactory.get("connectionPool.unableToCancelConnectionFuture"), 
x);
             } finally {
                 // Whatever came out of it, the callers waiting on the claim 
are
                 // done waiting
@@ -2314,10 +2301,10 @@ public class ConnectionPool {
             this.pool = new WeakReference<>(pool);
             this.sleepTime = sleepTime;
             if (sleepTime <= 0) {
-                log.warn("Database connection pool evicter thread interval is 
set to 0, defaulting to 30 seconds");
+                
log.warn(MessageFactory.get("connectionPool.evicterIntervalZero"));
                 this.sleepTime = 1000 * 30;
             } else if (sleepTime < 1000) {
-                log.warn("Database connection pool evicter thread interval is 
set to lower than 1 second.");
+                
log.warn(MessageFactory.get("connectionPool.evicterIntervalTooLow"));
             }
         }
 
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java
index 76edea778d..a7f2a420bc 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java
@@ -26,6 +26,7 @@ import javax.management.ObjectName;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 
 /**
@@ -152,14 +153,14 @@ public class DataSource extends DataSourceProxy 
implements javax.sql.DataSource,
             }
         } catch (MalformedObjectNameException x) {
             this.oname = null;
-            log.error("Unable to create object name for JDBC pool.", x);
+            
log.error(MessageFactory.get("dataSource.unableToCreateObjectName"), x);
         } catch (RuntimeException x) {
             // Never break the caller's MBean registration because of
             // pool-side state (for instance a pool that has not been
             // created yet); preRegister throwing aborts the whole
             // registration in the MBean server.
             this.oname = null;
-            log.error("Unable to register JDBC pool MBean.", x);
+            log.error(MessageFactory.get("dataSource.unableToRegisterMBean"), 
x);
         }
         return name;
     }
@@ -207,8 +208,7 @@ public class DataSource extends DataSourceProxy implements 
javax.sql.DataSource,
         // spurious error log
         final org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmx = p == null ? 
null : p.getJmxPool();
         if (jmx == null) {
-            log.warn("JDBC pool MBean [" + oname + "] not registered: the pool 
" +
-                    "has not been created yet or has already been closed.");
+            log.warn(MessageFactory.get("dataSource.mbeanNotRegistered", 
oname));
             // oname is the handle postDeregister() unregisters; do not keep
             // one that was never registered
             this.oname = null;
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
index 80f21f7f69..767a9e34ad 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
@@ -38,6 +38,7 @@ import javax.sql.DataSource;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * <p>
@@ -421,7 +422,7 @@ public class DataSourceFactory implements ObjectFactory {
         }
 
         if (!ok) {
-            log.warn(ref.getClassName() + " is not a valid class name/type for 
this JNDI factory.");
+            log.warn(MessageFactory.get("dataSourceFactory.invalidClassName", 
ref.getClassName()));
             return null;
         }
 
@@ -457,7 +458,7 @@ public class DataSourceFactory implements ObjectFactory {
             String addressName = addresses.nextElement().getType();
             if (!ALL_PROPERTY_NAMES.contains(addressName)
                     && !RESERVED_JNDI_ADDRESS_NAMES.contains(addressName)) {
-                log.warn(addressName + " is not a valid property, it will be 
ignored.");
+                
log.warn(MessageFactory.get("dataSourceFactory.invalidProperty", addressName));
             }
         }
 
@@ -589,7 +590,7 @@ public class DataSourceFactory implements ObjectFactory {
 
         value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN);
         if (value != null) {
-            log.warn(PROP_NUMTESTSPEREVICTIONRUN + " is not a valid setting, 
it will have no effect.");
+            log.warn(MessageFactory.get("dataSourceFactory.invalidSetting", 
PROP_NUMTESTSPEREVICTIONRUN));
             poolProperties.setNumTestsPerEvictionRun(Integer.parseInt(value));
         }
 
@@ -643,7 +644,7 @@ public class DataSourceFactory implements ObjectFactory {
 
         value = 
properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
         if (value != null) {
-            log.warn(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED + " is not a 
valid setting, it will have no effect.");
+            log.warn(MessageFactory.get("dataSourceFactory.invalidSetting", 
PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED));
             Boolean b = parseBoolean(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, 
value);
             if (b != null) {
                 
poolProperties.setAccessToUnderlyingConnectionAllowed(b.booleanValue());
@@ -673,12 +674,12 @@ public class DataSourceFactory implements ObjectFactory {
 
         value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS);
         if (value != null) {
-            log.warn(PROP_POOLPREPAREDSTATEMENTS + " is not a valid setting, 
it will have no effect.");
+            log.warn(MessageFactory.get("dataSourceFactory.invalidSetting", 
PROP_POOLPREPAREDSTATEMENTS));
         }
 
         value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS);
         if (value != null) {
-            log.warn(PROP_MAXOPENPREPAREDSTATEMENTS + " is not a valid 
setting, it will have no effect.");
+            log.warn(MessageFactory.get("dataSourceFactory.invalidSetting", 
PROP_MAXOPENPREPAREDSTATEMENTS));
         }
 
         value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
@@ -762,10 +763,8 @@ public class DataSourceFactory implements ObjectFactory {
             // point: getObjectInstance hands an actual javax.sql.DataSource
             // RefAddr content straight to the pool instead of stringifying it.
             // A string cannot be turned into a data source here.
-            throw new IllegalArgumentException("Can't set dataSource property 
as a string \"" + value +
-                    "\". Either carry the javax.sql.DataSource object itself 
under the dataSource attribute" +
-                    " of the Reference, or use the dataSourceJNDI attribute to 
name a data source bound in" +
-                    " JNDI.");
+            throw new 
IllegalArgumentException(MessageFactory.get("dataSourceFactory.cannotSetDataSourceString",
+                    value));
         }
 
         value = properties.getProperty(PROP_DATASOURCE_JNDI);
@@ -868,7 +867,7 @@ public class DataSourceFactory implements ObjectFactory {
         if ("false".equalsIgnoreCase(trimmed)) {
             return Boolean.FALSE;
         }
-        throw new IllegalArgumentException("Invalid boolean value '" + value + 
"' for property " + name);
+        throw new 
IllegalArgumentException(MessageFactory.get("dataSourceFactory.invalidBoolean", 
value, name));
     }
 
     /**
@@ -951,7 +950,7 @@ public class DataSourceFactory implements ObjectFactory {
             if (context != null) {
                 jndiDS = context.lookup(poolProperties.getDataSourceJNDI());
             } else {
-                log.warn("dataSourceJNDI property is configured, but local 
JNDI context is null.");
+                
log.warn(MessageFactory.get("dataSourceFactory.jndiContextNull"));
             }
         } catch (NamingException e) {
             if (log.isDebugEnabled()) {
@@ -967,19 +966,17 @@ public class DataSourceFactory implements ObjectFactory {
                 jndiDS = context.lookup(poolProperties.getDataSourceJNDI());
             } catch (NamingException e) {
                 initialContextFailure = e;
-                log.warn("The name \"" + poolProperties.getDataSourceJNDI() +
-                        "\" cannot be found in the InitialContext.", e);
+                
log.warn(MessageFactory.get("dataSourceFactory.jndiNameNotFound",
+                        poolProperties.getDataSourceJNDI()), e);
             }
         }
         if (jndiDS != null) {
             poolProperties.setDataSource(jndiDS);
             return;
         }
-        NamingException failure = new NamingException("Unable to create the 
connection pool: the name [" +
-                poolProperties.getDataSourceJNDI() + "] configured through the 
'dataSourceJNDI' attribute was" +
-                " not found in the local JNDI context nor in the 
InitialContext. The pool is built around the" +
-                " data source that name refers to; check that the name is 
correct and that the data source is" +
-                " bound and visible to the application before the pool is 
created.");
+        NamingException failure =
+                new 
NamingException(MessageFactory.get("dataSourceFactory.jndiLookupFailed",
+                        poolProperties.getDataSourceJNDI()));
         if (initialContextFailure != null) {
             failure.setRootCause(initialContextFailure);
         }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
index 82cddbb5ec..5aaec9cdf8 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
@@ -30,6 +30,7 @@ import javax.sql.XAConnection;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * The DataSource proxy lets us implements methods that don't exist in the 
current compiler JDK but might be methods
@@ -91,7 +92,7 @@ public class DataSourceProxy implements PoolConfiguration {
      */
     public DataSourceProxy(PoolConfiguration poolProperties) {
         if (poolProperties == null) {
-            throw new NullPointerException("PoolConfiguration cannot be 
null.");
+            throw new 
NullPointerException(MessageFactory.get("dataSourceProxy.poolConfigurationNull"));
         }
         this.poolProperties = poolProperties;
     }
@@ -155,7 +156,7 @@ public class DataSourceProxy implements PoolConfiguration {
         // The Wrapper contract asks for a failure, not for a silent null
         // that turns into an unrelated NullPointerException at the caller's
         // first use of the "unwrapped" value.
-        throw new SQLException("Not a wrapper of " + iface.getName());
+        throw new 
SQLException(MessageFactory.get("dataSourceProxy.notAWrapper", 
iface.getName()));
     }
 
     /**
@@ -177,10 +178,7 @@ public class DataSourceProxy implements PoolConfiguration {
             return createPool().getConnection(username, password);
         } else {
             if (connectionWithoutCredentialsWarningIssued.compareAndSet(false, 
true)) {
-                log.warn(String.format(
-                        "alternateUsernameAllowed is set to false so the 
provided user name [%s] and associated " +
-                                "password have been ignored",
-                        username));
+                
log.warn(MessageFactory.get("dataSourceProxy.alternateUsernameIgnored", 
username));
             }
             return getConnection();
         }
@@ -229,7 +227,7 @@ public class DataSourceProxy implements PoolConfiguration {
         // Returning null is not possible here: either the closed flag
         // rejects the caller or a pool is published and returned.
         if (closed) {
-            throw new SQLException("DataSource has been closed.");
+            throw new 
SQLException(MessageFactory.get("dataSourceProxy.dataSourceClosed"));
         }
         synchronized (poolCreationLock) {
             // Re-check under the gate: a close completed while this thread
@@ -237,7 +235,7 @@ public class DataSourceProxy implements PoolConfiguration {
             // gate has already published its pool. The gate also keeps this
             // the only construction the shared poolProperties ever faces.
             if (closed) {
-                throw new SQLException("DataSource has been closed.");
+                throw new 
SQLException(MessageFactory.get("dataSourceProxy.dataSourceClosed"));
             }
             final ConnectionPool existing = pool;
             if (existing != null) {
@@ -269,9 +267,9 @@ public class DataSourceProxy implements PoolConfiguration {
                 try {
                     fresh.close(true);
                 } catch (Exception x) {
-                    log.warn("Error during connection pool closure.", x);
+                    
log.warn(MessageFactory.get("dataSourceProxy.errorClosingPool"), x);
                 }
-                throw new SQLException("DataSource has been closed.");
+                throw new 
SQLException(MessageFactory.get("dataSourceProxy.dataSourceClosed"));
             }
             return fresh;
         }
@@ -321,7 +319,7 @@ public class DataSourceProxy implements PoolConfiguration {
             } catch (Exception ignore) {
                 // Ignore
             }
-            throw new SQLException("Connection from pool does not implement 
javax.sql.XAConnection");
+            throw new 
SQLException(MessageFactory.get("dataSourceProxy.connectionNotXA"));
         }
     }
 
@@ -345,7 +343,7 @@ public class DataSourceProxy implements PoolConfiguration {
             } catch (Exception ignore) {
                 // Ignore
             }
-            throw new SQLException("Connection from pool does not implement 
javax.sql.XAConnection");
+            throw new 
SQLException(MessageFactory.get("dataSourceProxy.connectionNotXA"));
         }
     }
 
@@ -389,7 +387,7 @@ public class DataSourceProxy implements PoolConfiguration {
                 // a normal lifecycle state, not a creation failure.
                 return null;
             }
-            log.error("Error during connection pool creation.", x);
+            log.error(MessageFactory.get("dataSourceProxy.errorCreatingPool"), 
x);
             return null;
         }
     }
@@ -430,7 +428,7 @@ public class DataSourceProxy implements PoolConfiguration {
             try {
                 p.close(all);
             } catch (Exception x) {
-                log.warn("Error during connection pool closure.", x);
+                
log.warn(MessageFactory.get("dataSourceProxy.errorClosingPool"), x);
             }
         }
     }
@@ -474,7 +472,7 @@ public class DataSourceProxy implements PoolConfiguration {
         // accessors would silently degrade. The class invariant is that this
         // field is never null, so reject the misuse at its site.
         if (poolProperties == null) {
-            throw new NullPointerException("PoolConfiguration cannot be 
null.");
+            throw new 
NullPointerException(MessageFactory.get("dataSourceProxy.poolConfigurationNull"));
         }
         this.poolProperties = poolProperties;
     }
@@ -710,7 +708,7 @@ public class DataSourceProxy implements PoolConfiguration {
             // interface (PoolProperties itself and the pool's JMX wrapper).
             getPoolProperties().setConnectionProperties(properties);
         } catch (RuntimeException x) {
-            log.error("Unable to parse connection properties.", x);
+            
log.error(MessageFactory.get("dataSourceProxy.unableToParseConnectionProperties"),
 x);
             throw x;
         }
     }
@@ -1454,7 +1452,7 @@ public class DataSourceProxy implements PoolConfiguration 
{
         try {
             createPool().purge();
         } catch (SQLException x) {
-            log.error("Unable to purge pool.", x);
+            log.error(MessageFactory.get("dataSourceProxy.unableToPurgePool"), 
x);
         }
     }
 
@@ -1465,7 +1463,7 @@ public class DataSourceProxy implements PoolConfiguration 
{
         try {
             createPool().purgeOnReturn();
         } catch (SQLException x) {
-            log.error("Unable to purge pool.", x);
+            log.error(MessageFactory.get("dataSourceProxy.unableToPurgePool"), 
x);
         }
     }
 }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java
index 8b1bf25434..65f2220ba7 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java
@@ -20,6 +20,8 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.sql.SQLException;
 
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
+
 /**
  * A DisposableConnectionFacade object is the top most interceptor that wraps 
an object of type
  * {@link PooledConnection}. The DisposableConnectionFacade intercepts three 
methods:
@@ -123,7 +125,7 @@ public class DisposableConnectionFacade extends 
JdbcInterceptor {
                 if (compare(TOSTRING_VAL, method)) {
                     return "DisposableConnectionFacade[null]";
                 }
-                throw new SQLException("PooledConnection has already been 
closed.");
+                throw new 
SQLException(MessageFactory.get("disposableConnectionFacade.pooledConnectionClosed"));
             }
 
             throw e;
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java
index f261ad9254..c07cff065a 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java
@@ -32,6 +32,8 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.ReentrantLock;
 
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
+
 /**
  * A simple implementation of a blocking queue with fairness waiting. 
invocations to method poll(...) will get handed
  * out in the order they were received. Locking is fine grained, a shared lock 
is only used during the first level of
@@ -354,7 +356,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public int drainTo(Collection<? super E> c, int maxElements) {
-        throw new UnsupportedOperationException("int drainTo(Collection<? 
super E> c, int maxElements)");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedDrainTo"));
     }
 
     /**
@@ -401,7 +403,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public void clear() {
-        throw new UnsupportedOperationException("void clear()");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedClear"));
 
     }
 
@@ -412,7 +414,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public boolean containsAll(Collection<?> c) {
-        throw new UnsupportedOperationException("boolean 
containsAll(Collection<?> c)");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedContainsAll"));
     }
 
     @Override
@@ -427,7 +429,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public boolean removeAll(Collection<?> c) {
-        throw new UnsupportedOperationException("boolean 
removeAll(Collection<?> c)");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedRemoveAll"));
     }
 
     /**
@@ -437,7 +439,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public boolean retainAll(Collection<?> c) {
-        throw new UnsupportedOperationException("boolean 
retainAll(Collection<?> c)");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedRetainAll"));
     }
 
     /**
@@ -447,7 +449,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public Object[] toArray() {
-        throw new UnsupportedOperationException("Object[] toArray()");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedToArray"));
     }
 
     /**
@@ -457,7 +459,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public <T> T[] toArray(T[] a) {
-        throw new UnsupportedOperationException("<T> T[] toArray(T[] a)");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedToArrayT"));
     }
 
     /**
@@ -467,7 +469,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public E element() {
-        throw new UnsupportedOperationException("E element()");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedElement"));
     }
 
     /**
@@ -477,7 +479,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public E peek() {
-        throw new UnsupportedOperationException("E peek()");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedPeek"));
     }
 
     /**
@@ -487,7 +489,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
      */
     @Override
     public E remove() {
-        throw new UnsupportedOperationException("E remove()");
+        throw new 
UnsupportedOperationException(MessageFactory.get("fairBlockingQueue.unsupportedRemove"));
     }
 
 
@@ -591,7 +593,8 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
                 }
                 return latch.getItem();
             } else {
-                throw new ExecutionException("ItemFuture incorrectly 
instantiated. Bug in the code?", new Exception());
+                throw new 
ExecutionException(MessageFactory.get("fairBlockingQueue.itemFutureInstantiationError"),
+                        new Exception());
             }
         }
 
@@ -661,7 +664,8 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
                     return latch.getItem();
                 }
             } else {
-                throw new ExecutionException("ItemFuture incorrectly 
instantiated. Bug in the code?", new Exception());
+                throw new 
ExecutionException(MessageFactory.get("fairBlockingQueue.itemFutureInstantiationError"),
+                        new Exception());
             }
         }
 
@@ -683,8 +687,7 @@ public class FairBlockingQueue<E> implements 
BlockingQueue<E> {
          * @return the timeout failure to throw
          */
         private TimeoutException abandonedTimeout() {
-            return new TimeoutException("The waiter of this future timed out 
on an earlier" +
-                    " get() call and was deregistered from the queue; the 
future can no" + " longer receive an item");
+            return new 
TimeoutException(MessageFactory.get("fairBlockingQueue.itemFutureAbandoned"));
         }
 
         /**
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
index d3c744574e..bec02a2305 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
@@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Implementation of {@link PoolConfiguration} that holds the configuration 
properties for a connection pool.
@@ -477,8 +478,9 @@ public class PoolProperties implements PoolConfiguration, 
Cloneable, Serializabl
                             }
                             int pidx = prop.indexOf('=');
                             if (pidx < 0) {
-                                throw new IllegalArgumentException("Invalid 
interceptor property '" + prop.trim() +
-                                        "', missing '=', in jdbcInterceptors 
value [" + interceptorValues[i] + "]");
+                                throw new 
IllegalArgumentException(MessageFactory.get(
+                                        
"poolProperties.invalidInterceptorProperty", prop.trim(),
+                                        interceptorValues[i]));
                             }
                             String propName = prop.substring(0, pidx).trim();
                             String propValue = prop.substring(pidx + 1).trim();
@@ -727,14 +729,13 @@ public class PoolProperties implements PoolConfiguration, 
Cloneable, Serializabl
                     PoolProperties.class.getClassLoader(), 
Thread.currentThread().getContextClassLoader());
             validator = validatorClass.getConstructor().newInstance();
         } catch (ClassNotFoundException e) {
-            log.warn("The class " + className + " cannot be found.", e);
+            
log.warn(MessageFactory.get("poolProperties.validatorClassNotFound", 
className), e);
         } catch (ClassCastException e) {
-            log.warn("The class " + className + " does not implement the 
Validator interface.", e);
+            
log.warn(MessageFactory.get("poolProperties.validatorNotValidator", className), 
e);
         } catch (IllegalAccessException e) {
-            log.warn("The class " + className + " or its no-arg constructor 
are inaccessible.", e);
+            
log.warn(MessageFactory.get("poolProperties.validatorInaccessible", className), 
e);
         } catch (ReflectiveOperationException | IllegalArgumentException | 
SecurityException e) {
-            log.warn("An object of class " + className + " cannot be 
instantiated. Make sure that " +
-                    "it includes an implicit or explicit no-arg constructor.", 
e);
+            
log.warn(MessageFactory.get("poolProperties.validatorCannotInstantiate", 
className), e);
         }
     }
 
@@ -1315,7 +1316,7 @@ public class PoolProperties implements PoolConfiguration, 
Cloneable, Serializabl
     @Override
     public void setDataSource(Object ds) {
         if (ds instanceof DataSourceProxy) {
-            throw new IllegalArgumentException("Layered pools are not 
allowed.");
+            throw new 
IllegalArgumentException(MessageFactory.get("poolProperties.layeredPoolsNotAllowed"));
         }
         this.dataSource = ds;
     }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
index 78f8fdf872..8ecbf78d54 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
@@ -32,6 +32,7 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
 import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Represents a pooled connection and holds a reference to the {@link 
java.sql.Connection} object
@@ -235,7 +236,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
      */
     public void connect() throws SQLException {
         if (released.get()) {
-            throw new SQLException("A connection once released, can't be 
reestablished.");
+            throw new 
SQLException(MessageFactory.get("pooledConnection.cannotReestablish"));
         }
         if (connection != null) {
             try {
@@ -356,7 +357,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 // Contract-violating provider: a handle without a logical
                 // connection is of no use either.
                 failure = new SQLException(
-                        "XADataSource " + xds + " returned a null connection 
for the XA connection handle");
+                        
MessageFactory.get("pooledConnection.xaDataSourceNullConnection", xds));
             }
             if (failure != null || failureRuntime != null) {
                 // Do not strand the handle if the logical connection
@@ -388,7 +389,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 // when no connection can be provided; keeping that null would
                 // surface later as a misleading validation failure, or as a
                 // null pointer when the default connection state is applied.
-                throw new SQLException("DataSource " + ds + " returned a null 
connection");
+                throw new 
SQLException(MessageFactory.get("pooledConnection.dataSourceNullConnection", 
ds));
             }
         } else if (poolProperties.getDataSource() instanceof 
javax.sql.ConnectionPoolDataSource) {
             javax.sql.ConnectionPoolDataSource ds = 
(javax.sql.ConnectionPoolDataSource) poolProperties.getDataSource();
@@ -410,8 +411,8 @@ public class PooledConnection implements 
PooledConnectionMBean {
             if (connection == null && failure == null && failureRuntime == 
null) {
                 // Contract-violating provider: a handle without a logical
                 // connection is of no use either.
-                failure = new SQLException("ConnectionPoolDataSource " + ds +
-                        " returned a null connection for the pooled connection 
handle");
+                failure = new SQLException(
+                        
MessageFactory.get("pooledConnection.connectionPoolDataSourceNullConnection", 
ds));
             }
             if (failure != null || failureRuntime != null) {
                 // Do not strand the handle if the logical connection
@@ -432,8 +433,8 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 throw failure;
             }
         } else {
-            throw new SQLException("DataSource is of unknown class:" +
-                    (poolProperties.getDataSource() != null ? 
poolProperties.getDataSource().getClass() : "null"));
+            throw new 
SQLException(MessageFactory.get("pooledConnection.unknownDataSource",
+                    (poolProperties.getDataSource() != null ? 
poolProperties.getDataSource().getClass() : "null")));
         }
     }
 
@@ -519,7 +520,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
             }
         }
         if (connection == null) {
-            throw new SQLException("Driver:" + driver + " returned null for 
URL:" + driverURL);
+            throw new 
SQLException(MessageFactory.get("pooledConnection.driverReturnedNull", driver, 
driverURL));
         }
     }
 
@@ -575,8 +576,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 // catch only guards against a failure of the walk itself;
                 // an interceptor must not be able to keep the physical
                 // connection from being closed
-                log.warn("Interceptor threw an exception from the " + 
"disconnected() event; ignoring it so that the " +
-                        "physical connection can be closed.", x);
+                
log.warn(MessageFactory.get("pooledConnection.interceptorDisconnectedFailed"), 
x);
             }
             try {
                 if (cpdsConnection != null) {
@@ -715,7 +715,8 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 // between the idle and busy queues and strands the pooled
                 // connection - permanently shrinking the pool
                 if (getPoolProperties().getLogValidationErrors()) {
-                    log.error("Custom validation through " + 
poolProperties.getValidator() + " threw an exception.", x);
+                    
log.error(MessageFactory.get("pooledConnection.customValidationThrew",
+                            poolProperties.getValidator()), x);
                 } else if (log.isDebugEnabled()) {
                     log.debug("Custom validation threw an exception.", x);
                 }
@@ -726,7 +727,8 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 return true;
             } else {
                 if (getPoolProperties().getLogValidationErrors()) {
-                    log.error("Custom validation through " + 
poolProperties.getValidator() + " failed.");
+                    
log.error(MessageFactory.get("pooledConnection.customValidationFailed",
+                            poolProperties.getValidator()));
                 }
                 return false;
             }
@@ -771,7 +773,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
                     return transactionTerminated;
                 } else {
                     if (getPoolProperties().getLogValidationErrors()) {
-                        log.error("isValid() returned false.");
+                        
log.error(MessageFactory.get("pooledConnection.isValidReturnedFalse"));
                     }
                     return false;
                 }
@@ -784,7 +786,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 // idle and busy queues and strands the pooled connection -
                 // permanently shrinking the pool
                 if (getPoolProperties().getLogValidationErrors()) {
-                    log.error("isValid() failed.", e);
+                    
log.error(MessageFactory.get("pooledConnection.isValidFailed"), e);
                 } else if (e instanceof RuntimeException) {
                     // Unlike the checked failure this anomaly would once
                     // have escaped as the driver's own throw; the caller
@@ -793,7 +795,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
                     // than debug - otherwise at the default log level a
                     // broken driver would fail every validation with no
                     // trace anywhere
-                    log.warn("isValid() failed.", e);
+                    
log.warn(MessageFactory.get("pooledConnection.isValidFailed"), e);
                 } else if (log.isDebugEnabled()) {
                     log.debug("isValid() failed.", e);
                 }
@@ -841,7 +843,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
             return transactionTerminated;
         } catch (Exception ex) {
             if (getPoolProperties().getLogValidationErrors()) {
-                log.error("SQL Validation error", ex);
+                
log.error(MessageFactory.get("pooledConnection.sqlValidationError"), ex);
             } else if (log.isDebugEnabled()) {
                 log.debug("Unable to validate object:", ex);
             }
@@ -882,14 +884,14 @@ public class PooledConnection implements 
PooledConnectionMBean {
             log.debug("Failed to commit transaction", e);
         } catch (RuntimeException e) {
             if (getPoolProperties().getLogValidationErrors()) {
-                log.error("Failed to commit transaction", e);
+                log.error(MessageFactory.get("pooledConnection.commitFailed"), 
e);
             } else {
                 // The unchecked variant is logged at warn rather than debug,
                 // like the rollback helper's: the caller surfaces it only as
                 // the generic validation failure, so at the default log level
                 // a driver that fails to commit would otherwise destroy
                 // connection after connection with no trace anywhere
-                log.warn("Failed to commit transaction", e);
+                log.warn(MessageFactory.get("pooledConnection.commitFailed"), 
e);
             }
         }
         return false;
@@ -913,7 +915,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
             // converts it, and strand the pooled connection between the
             // idle and busy queues
             if (getPoolProperties().getLogValidationErrors()) {
-                log.error("Failed to rollback transaction", e);
+                
log.error(MessageFactory.get("pooledConnection.rollbackFailed"), e);
             } else {
                 // The unchecked variant is an anomaly of the same kind as
                 // a throwing custom Validator, and unlike the checked
@@ -923,7 +925,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
                 // level a driver that fails to end a transaction unchecked
                 // would destroy connection after connection with no trace
                 // anywhere
-                log.warn("Failed to rollback transaction", e);
+                
log.warn(MessageFactory.get("pooledConnection.rollbackFailed"), e);
             }
         }
         return false;
@@ -1045,7 +1047,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
      */
     public void setDiscarded(boolean discarded) {
         if (this.discarded && !discarded) {
-            throw new IllegalStateException("Unable to change the state once 
the connection has been discarded");
+            throw new 
IllegalStateException(MessageFactory.get("pooledConnection.discardedStateChange"));
         }
         this.discarded = discarded;
     }
@@ -1199,8 +1201,8 @@ public class PooledConnection implements 
PooledConnectionMBean {
                     } catch (Throwable t) {
                         // One broken interceptor must not stop the rest of the
                         // chain from being reset
-                        log.warn("Interceptor [" + 
interceptor.getClass().getName() +
-                                "] threw an exception from reset(); " + 
"continuing with the rest of the chain.", t);
+                        
log.warn(MessageFactory.get("pooledConnection.interceptorResetFailed",
+                                interceptor.getClass().getName()), t);
                     }
                     interceptor = next;
                 }
@@ -1278,7 +1280,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
     private java.sql.Connection requireConnection() throws SQLException {
         java.sql.Connection c = connection;
         if (c == null) {
-            throw new SQLException("Connection has been disconnected.");
+            throw new 
SQLException(MessageFactory.get("pooledConnection.disconnected"));
         }
         return c;
     }
@@ -1297,7 +1299,7 @@ public class PooledConnection implements 
PooledConnectionMBean {
         try {
             c.clearWarnings();
         } catch (SQLException e) {
-            log.warn("Unable to clear warnings on the connection.", e);
+            
log.warn(MessageFactory.get("pooledConnection.unableToClearWarnings"), e);
         }
     }
 
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
index 710a853e0a..3119539719 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
@@ -23,6 +23,8 @@ import java.sql.SQLException;
 
 import javax.sql.XAConnection;
 
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
+
 /**
  * A ProxyConnection object is the bottom most interceptor that wraps an 
object of type {@link PooledConnection}. The
  * ProxyConnection intercepts, among the calls it forwards, the following 
methods:
@@ -197,7 +199,7 @@ public class ProxyConnection extends JdbcInterceptor {
                 // what isWrapperFor() says and turn into an unrelated
                 // NullPointerException at the caller's first use of the
                 // "unwrapped" value.
-                throw new SQLException("Not a wrapper of " + iface.getName());
+                throw new 
SQLException(MessageFactory.get("proxyConnection.notAWrapper", 
iface.getName()));
             }
             return connection;
         } else if (iface == XAConnection.class) {
@@ -206,7 +208,7 @@ public class ProxyConnection extends JdbcInterceptor {
                 // The Wrapper contract asks for a failure, not for a silent
                 // null that turns into an unrelated NullPointerException at
                 // the caller's first use of the "unwrapped" value.
-                throw new SQLException("Not a wrapper of " + iface.getName());
+                throw new 
SQLException(MessageFactory.get("proxyConnection.notAWrapper", 
iface.getName()));
             }
             return xa;
         } else if (iface.isInstance(connection.getConnection())) {
@@ -216,7 +218,7 @@ public class ProxyConnection extends JdbcInterceptor {
             // unwrapping that far is its answer to give, not ours
             return connection.getConnection().unwrap(iface);
         } else {
-            throw new SQLException("Not a wrapper of " + iface.getName());
+            throw new 
SQLException(MessageFactory.get("proxyConnection.notAWrapper", 
iface.getName()));
         }
     }
 
@@ -349,10 +351,9 @@ public class ProxyConnection extends JdbcInterceptor {
                     // The handle was disconnected while borrowed; report it
                     // the way every other method on this class does, rather
                     // than blaming the XA configuration.
-                    throw new SQLException("Connection has already been 
closed.");
+                    throw new 
SQLException(MessageFactory.get("proxyConnection.connectionClosed"));
                 }
-                throw new SQLException("The pooled connection is not an XA 
connection; " + "method [" +
-                        method.getName() + "] cannot be dispatched.");
+                throw new 
SQLException(MessageFactory.get("proxyConnection.notXAConnection", 
method.getName()));
             }
             try {
                 return method.invoke(xaConnection, args);
@@ -365,7 +366,7 @@ public class ProxyConnection extends JdbcInterceptor {
             }
         }
         if (isClosed()) {
-            throw new SQLException("Connection has already been closed.");
+            throw new 
SQLException(MessageFactory.get("proxyConnection.connectionClosed"));
         }
         if (compare(UNWRAP_VAL, method)) {
             Class<?> iface = (Class<?>) args[0];
@@ -392,7 +393,7 @@ public class ProxyConnection extends JdbcInterceptor {
             if (poolc != null) {
                 return method.invoke(poolc.getConnection(), args);
             } else {
-                throw new SQLException("Connection has already been closed.");
+                throw new 
SQLException(MessageFactory.get("proxyConnection.connectionClosed"));
             }
         } catch (Throwable t) {
             if (t instanceof InvocationTargetException) {
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/StatementFacade.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/StatementFacade.java
index e289878f97..d67e5d6d4e 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/StatementFacade.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/StatementFacade.java
@@ -31,6 +31,7 @@ import java.sql.Wrapper;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import 
org.apache.tomcat.jdbc.pool.interceptor.AbstractCreateStatementInterceptor;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Facade that creates statement and result set proxies.
@@ -204,13 +205,12 @@ public class StatementFacade extends 
AbstractCreateStatementInterceptor {
                 }
             }
             if (target == null) {
-                throw new SQLException("Statement closed.");
+                throw new 
SQLException(MessageFactory.get("statementFacade.statementClosed"));
             }
             if (ended) {
                 // The delegate is still open, but it is open on a session
                 // that belongs to the next borrower now.
-                throw new SQLException("Statement closed. The pooled 
connection this " +
-                        "statement was created from has been returned to the 
pool.");
+                throw new 
SQLException(MessageFactory.get("statementFacade.statementClosedConnectionReturned"));
             }
             // The connection a statement reports is the pooled handle the
             // application created it from, not the driver's own connection.
@@ -345,13 +345,12 @@ public class StatementFacade extends 
AbstractCreateStatementInterceptor {
                 }
             }
             if (target == null) {
-                throw new SQLException("ResultSet closed.");
+                throw new 
SQLException(MessageFactory.get("statementFacade.resultSetClosed"));
             }
             if (ended) {
                 // The cursor is still open, but it is open on a session that
                 // belongs to the next borrower now.
-                throw new SQLException("ResultSet closed. The pooled 
connection this " +
-                        "result set was read from has been returned to the 
pool.");
+                throw new 
SQLException(MessageFactory.get("statementFacade.resultSetClosedConnectionReturned"));
             }
 
             if (compare(GET_STATEMENT, method)) {
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java
index 36540b75ef..4a447b4ba8 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java
@@ -21,6 +21,8 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.SQLException;
 
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
+
 /**
  * Interceptor that traps any unhandled exception types and throws an 
exception that has been declared by the method
  * called, or throw an SQLException if it is declared. If the caught exception 
is not declared, and the method doesn't
@@ -50,11 +52,13 @@ public class TrapException extends JdbcInterceptor {
             Class<?> exceptionClass = exception.getClass();
             if (!isDeclaredException(method, exceptionClass)) {
                 if (isDeclaredException(method, SQLException.class)) {
-                    SQLException sqlx = new SQLException("Uncaught underlying 
exception.");
+                    SQLException sqlx =
+                            new 
SQLException(MessageFactory.get("trapException.uncaughtUnderlyingException"));
                     sqlx.initCause(exception);
                     exception = sqlx;
                 } else {
-                    RuntimeException rx = new RuntimeException("Uncaught 
underlying exception.");
+                    RuntimeException rx =
+                            new 
RuntimeException(MessageFactory.get("trapException.uncaughtUnderlyingException"));
                     rx.initCause(exception);
                     exception = rx;
                 }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
index 15120840db..f0a841fd88 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
@@ -20,6 +20,8 @@ import java.sql.SQLException;
 
 import javax.sql.XAConnection;
 
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
+
 /**
  * XA-enabled data source for distributed transaction support.
  * <p>
@@ -93,19 +95,11 @@ public class XADataSource extends DataSource implements 
javax.sql.XADataSource {
             // configured pool therefore never becomes a data source at all.
             String jndiName = getPoolProperties().getDataSourceJNDI();
             if (jndiName != null) {
-                throw new SQLException("The connection pool [" + getName() + 
"] cannot provide XA connections:" +
-                        " the name \"" + jndiName + "\" set through the 
'dataSourceJNDI' attribute has not been" +
-                        " resolved into a data source; the name is only looked 
up when the pool is created" +
-                        " through the DataSourceFactory. Set the 'dataSource' 
attribute to a" +
-                        " javax.sql.XADataSource provided by the JDBC 
driver.");
+                throw new 
SQLException(MessageFactory.get("xaDataSource.jndiNotResolved", getName(), 
jndiName));
             }
-            throw new SQLException("The connection pool [" + getName() + "] 
cannot provide XA connections:" +
-                    " neither the 'dataSource' nor the 'dataSourceJNDI' 
attribute is set, so only plain JDBC" +
-                    " connections from a JDBC driver and URL can be pooled. 
Set the 'dataSource' or" +
-                    " 'dataSourceJNDI' attribute to a javax.sql.XADataSource 
provided by the JDBC driver.");
+            throw new 
SQLException(MessageFactory.get("xaDataSource.noDataSource", getName()));
         }
-        throw new SQLException("The connection pool [" + getName() + "] cannot 
provide XA connections: the data" +
-                " source configured through the 'dataSource' or 
'dataSourceJNDI' attribute (" +
-                ds.getClass().getName() + ") does not implement 
javax.sql.XADataSource.");
+        throw new 
SQLException(MessageFactory.get("xaDataSource.dataSourceNotXA", getName(),
+                ds.getClass().getName()));
     }
 }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
index 6bd27ccc7b..df48876908 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
@@ -31,6 +31,7 @@ import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.ConnectionPool;
 import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Abstraction interceptor. This component intercepts all calls to create some 
type of SQL statement. By extending this
@@ -170,8 +171,8 @@ public abstract class AbstractCreateStatementInterceptor 
extends JdbcInterceptor
         } catch (ReflectiveOperationException | IllegalArgumentException | 
LinkageError e) {
             // An exception escaping the class initializer would surface as an
             // ExceptionInInitializerError at every use of every subclass.
-            log.warn("Unable to create the constructor cache entry for index " 
+ idx + " (" + clazz.getName()
-                    + ").", e);
+            
log.warn(MessageFactory.get("abstractCreateStatementInterceptor.unableToCreateConstructorCacheEntry",
+                    idx, clazz.getName()), e);
         }
     }
 
@@ -237,10 +238,9 @@ public abstract class AbstractCreateStatementInterceptor 
extends JdbcInterceptor
                     new Class[] { clazz });
             constructors[idx] = proxyClass.getConstructor(new Class[] { 
InvocationHandler.class });
         } else if 
(!clazz.isAssignableFrom(constructors[idx].getDeclaringClass())) {
-            throw new IllegalStateException("Constructor cache index " + idx + 
" is already populated for "
-                    + 
Arrays.toString(constructors[idx].getDeclaringClass().getInterfaces()) + ", but 
"
-                    + clazz.getName() + " was requested. Every use of an index 
must pass an interface the cached"
-                    + " proxy implements.");
+            throw new IllegalStateException(MessageFactory.get(
+                    
"abstractCreateStatementInterceptor.constructorCacheIndexPopulated",
+                    idx, 
Arrays.toString(constructors[idx].getDeclaringClass().getInterfaces()), 
clazz.getName()));
         }
         return constructors[idx];
     }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
index 175bf44d02..e0e06e15d6 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
@@ -28,6 +28,7 @@ import java.sql.Statement;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Abstract class that wraps statements and intercepts query executions.
@@ -213,7 +214,7 @@ public abstract class AbstractQueryReport extends 
AbstractCreateStatementInterce
             result = constructor.newInstance(new Object[] { new 
StatementProxy(statement, sql) });
             return result;
         } catch (Exception x) {
-            log.warn("Unable to create statement proxy for slow query 
report.", x);
+            
log.warn(MessageFactory.get("abstractQueryReport.unableToCreateStatementProxy"),
 x);
         }
         return statement;
     }
@@ -316,7 +317,7 @@ public abstract class AbstractQueryReport extends 
AbstractCreateStatementInterce
             }
             // If we are calling anything else, bail out
             if (gone) {
-                throw new SQLException("Statement closed.");
+                throw new 
SQLException(MessageFactory.get("abstractQueryReport.statementClosed"));
             }
             boolean process = false;
             // Check to see if we are about to execute a query
@@ -358,8 +359,8 @@ public abstract class AbstractQueryReport extends 
AbstractCreateStatementInterce
                         // Reporting must never disturb the application -
                         // the driver's exception is the one being thrown
                         if (log.isWarnEnabled()) {
-                            log.warn("Unable to process failed query for " +
-                                    
AbstractQueryReport.this.getClass().getName(), reporting);
+                            
log.warn(MessageFactory.get("abstractQueryReport.unableToProcessFailedQuery",
+                                    
AbstractQueryReport.this.getClass().getName()), reporting);
                         }
                     }
                 }
@@ -387,8 +388,8 @@ public abstract class AbstractQueryReport extends 
AbstractCreateStatementInterce
                     reportSlowQuery(query, args, name, start, delta);
                 } catch (Throwable t) {
                     if (log.isWarnEnabled()) {
-                        log.warn("Unable to process slow query for " + 
AbstractQueryReport.this.getClass().getName(),
-                                t);
+                        
log.warn(MessageFactory.get("abstractQueryReport.unableToProcessSlowQuery",
+                                
AbstractQueryReport.this.getClass().getName()), t);
                     }
                 }
             } else if (process) {
@@ -397,8 +398,8 @@ public abstract class AbstractQueryReport extends 
AbstractCreateStatementInterce
                 } catch (Throwable t) {
                     // Reporting must never disturb the application
                     if (log.isWarnEnabled()) {
-                        log.warn("Unable to process query report for " + 
AbstractQueryReport.this.getClass().getName(),
-                                t);
+                        
log.warn(MessageFactory.get("abstractQueryReport.unableToProcessQueryReport",
+                                
AbstractQueryReport.this.getClass().getName()), t);
                     }
                 }
             }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java
index 5494e861ad..d5d1b6b842 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java
@@ -27,6 +27,7 @@ import org.apache.tomcat.jdbc.pool.DataSourceFactory;
 import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
 import org.apache.tomcat.jdbc.pool.PoolConfiguration;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Interceptor that keep track of connection state to avoid roundtrips to the 
database. The
@@ -163,8 +164,7 @@ public class ConnectionState extends JdbcInterceptor {
                 // ConnectionPool.setupConnection() catches this, discards the
                 // connection and fails the borrow.
                 throw new IllegalStateException(
-                        "Unable to check for, or terminate, the pending 
transaction before restoring the configured connection state.",
-                        x);
+                        
MessageFactory.get("connectionState.unableToTerminatePendingTransaction"), x);
             }
         }
         if (resetTransactionIsolation) {
@@ -189,9 +189,8 @@ public class ConnectionState extends JdbcInterceptor {
                 // Report it here, at refusal time, as no later borrow will log
                 // it again.
                 refusedIsolationLevel = defaultTransactionIsolation;
-                log.error("Unable to reset transaction isolation state to 
connection. The configured level "
-                        + defaultTransactionIsolation + " will not be retried 
on this session; it keeps its "
-                        + "current isolation level.", x);
+                
log.error(MessageFactory.get("connectionState.unableToResetTransactionIsolation",
+                        defaultTransactionIsolation), x);
             }
         }
         if (resetReadOnly) {
@@ -207,8 +206,8 @@ public class ConnectionState extends JdbcInterceptor {
                 // that failed, so it must not go to the next borrower.
                 // ConnectionPool.setupConnection() catches this, discards the
                 // connection and fails the borrow.
-                throw new IllegalStateException("Unable to restore the 
configured read-only state "
-                        + defaultReadOnly + " on the connection.", x);
+                throw new IllegalStateException(
+                        
MessageFactory.get("connectionState.unableToRestoreReadOnly", defaultReadOnly), 
x);
             }
         }
         if (resetAutoCommit) {
@@ -222,8 +221,8 @@ public class ConnectionState extends JdbcInterceptor {
                 // As for the read-only state above: the driver refused the
                 // configured auto-commit state, so the borrow fails and the
                 // connection is discarded.
-                throw new IllegalStateException("Unable to restore the 
configured auto-commit state "
-                        + defaultAutoCommit + " on the connection.", x);
+                throw new IllegalStateException(
+                        
MessageFactory.get("connectionState.unableToRestoreAutoCommit", 
defaultAutoCommit), x);
             }
         }
         if (resetCatalog) {
@@ -238,8 +237,8 @@ public class ConnectionState extends JdbcInterceptor {
                 // configured catalog, so the borrower learns of the
                 // misconfiguration instead of getting a connection that was
                 // never pointed at the database the pool asks it to work in.
-                throw new IllegalStateException("Unable to restore the 
configured default catalog "
-                        + defaultCatalog + " on the connection.", x);
+                throw new IllegalStateException(
+                        
MessageFactory.get("connectionState.unableToRestoreCatalog", defaultCatalog), 
x);
             }
         }
 
@@ -327,7 +326,7 @@ public class ConnectionState extends JdbcInterceptor {
             }
         } catch (Throwable e) {
             if (write) {
-                log.warn("Reset state to null as an exception occurred while 
calling method[" + name + "].", e);
+                
log.warn(MessageFactory.get("connectionState.resetStateToNull", name), e);
                 switch (index) {
                     case 0: {
                         autoCommit = null;
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java
index cc6a7c2481..367666236e 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java
@@ -26,6 +26,7 @@ import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.ConnectionPool;
 import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Interceptor that sets a query timeout on every created {@link Statement}.
@@ -95,11 +96,8 @@ public class QueryTimeoutInterceptor extends 
AbstractCreateStatementInterceptor
         try {
             timeout = Integer.parseInt(p.getValue());
         } catch (NumberFormatException x) {
-            log.warn("[QueryTimeoutInterceptor] The configured queryTimeout [" 
+ p.getValue()
-                    + "] is not an integer number of seconds and is not 
applied: statements created through"
-                    + " this configuration keep the default " + timeout + 
"-second query timeout. Correct the"
-                    + " value in the jdbcInterceptors configuration to set the 
timeout that was meant to be"
-                    + " configured.");
+            
log.warn(MessageFactory.get("queryTimeoutInterceptor.invalidQueryTimeout",
+                    p.getValue(), timeout));
         }
     }
 
@@ -142,10 +140,7 @@ public class QueryTimeoutInterceptor extends 
AbstractCreateStatementInterceptor
             return;
         }
         timeoutRefusalReported = true;
-        log.warn("[QueryTimeoutInterceptor] Unable to set the query timeout of 
" + timeout + " seconds on a statement"
-                + " of this connection: " + x.getMessage() + ". The driver 
does not apply the timeout, so statements"
-                + " created on this physical connection run without a query 
timeout. Refusals are not logged again on"
-                + " this connection.", x);
+        
log.warn(MessageFactory.get("queryTimeoutInterceptor.queryTimeoutRefused", 
timeout, x.getMessage()), x);
     }
 
     /**
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java
index 43833660d0..1a8767cf8b 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java
@@ -34,6 +34,7 @@ import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.ConnectionPool;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
 import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Class that resets the abandoned timer on any activity on the Connection, on 
any query execution and on any read of a
@@ -130,7 +131,8 @@ public class ResetAbandonedTimer extends 
AbstractQueryReport implements ResetAba
             }
             return getConstructor(index, type).newInstance(new Object[] { new 
TimerStatementProxy(result) });
         } catch (Exception x) {
-            log.warn("Unable to create timer statement proxy for " + 
getClass().getName() + ".", x);
+            
log.warn(MessageFactory.get("resetAbandonedTimer.unableToCreateTimerStatementProxy",
+                    getClass().getName()), x);
         }
         return result;
     }
@@ -234,7 +236,7 @@ public class ResetAbandonedTimer extends 
AbstractQueryReport implements ResetAba
                 return Boolean.TRUE;
             }
             if (target == null) {
-                throw new SQLException("Statement closed.");
+                throw new 
SQLException(MessageFactory.get("resetAbandonedTimer.statementClosed"));
             }
             Object result;
             try {
@@ -273,8 +275,8 @@ public class ResetAbandonedTimer extends 
AbstractQueryReport implements ResetAba
                 } catch (Exception x) {
                     // The application just does not get the timer reset
                     // while reading this result set
-                    log.warn("Unable to create timer result set proxy for " +
-                            ResetAbandonedTimer.this.getClass().getName() + 
".", x);
+                    
log.warn(MessageFactory.get("resetAbandonedTimer.unableToCreateTimerResultSetProxy",
+                            ResetAbandonedTimer.this.getClass().getName()), x);
                 }
             }
             return result;
@@ -365,7 +367,7 @@ public class ResetAbandonedTimer extends 
AbstractQueryReport implements ResetAba
                     // result set handle does, rather than by invoking on a
                     // null receiver: that is a NullPointerException, where the
                     // closed contract owes the application a SQLException
-                    throw new SQLException("ResultSet closed.");
+                    throw new 
SQLException(MessageFactory.get("resetAbandonedTimer.resultSetClosed"));
                 }
                 if (compare(GET_STATEMENT, method)) {
                     return stmt;
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
index ed26310890..fc5e671abe 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
@@ -37,6 +37,7 @@ import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.ConnectionPool;
 import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Slow query report interceptor. Tracks timing of query executions.
@@ -139,7 +140,7 @@ public class SlowQueryReport extends AbstractQueryReport {
         // FAILED QUERY notification behind isLogFailed() alone, the two
         // channels disagreeing on the same event.
         if (isLogFailed() && log.isWarnEnabled()) {
-            log.warn("Failed Query Report SQL=" + sql + "; time=" + delta + " 
ms;");
+            log.warn(MessageFactory.get("slowQueryReport.failedQueryReport", 
sql, delta));
         }
         return sql;
     }
@@ -179,7 +180,7 @@ public class SlowQueryReport extends AbstractQueryReport {
         // disagreeing on the same event. This mirrors how
         // reportFailedQuery reports its event.
         if (isLogSlow() && log.isWarnEnabled()) {
-            log.warn("Slow Query Report SQL=" + sql + "; time=" + delta + " 
ms;");
+            log.warn(MessageFactory.get("slowQueryReport.slowQueryReport", 
sql, delta));
         }
         return sql;
     }
@@ -245,9 +246,7 @@ public class SlowQueryReport extends AbstractQueryReport {
             return registration;
         });
         if (stats.hasOtherLivePools() && log.isWarnEnabled()) {
-            log.warn("Connection pool name [" + pool.getName() + "] is already 
in use by " +
-                    "another live pool; SlowQueryReport statistics are keyed 
by pool name, " +
-                    "so both pools report into the same statistics map.");
+            log.warn(MessageFactory.get("slowQueryReport.poolNameInUse", 
pool.getName()));
         }
         queries = stats.getQueryStats();
     }
@@ -315,7 +314,7 @@ public class SlowQueryReport extends AbstractQueryReport {
         ConcurrentHashMap<String, QueryStats> queries = 
SlowQueryReport.this.queries;
         if (queries == null) {
             if (log.isWarnEnabled()) {
-                log.warn("Connection has already been closed or abandoned");
+                
log.warn(MessageFactory.get("slowQueryReport.connectionClosedOrAbandoned"));
             }
             return null;
         }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java
index 84f8bf9e38..f79c457120 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java
@@ -45,6 +45,7 @@ import org.apache.tomcat.jdbc.pool.ConnectionPool;
 import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
 import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * JMX-enabled slow query report interceptor. Publishes slow and failed query 
statistics to JMX and provides
@@ -231,7 +232,7 @@ public class SlowQueryReportJmx extends SlowQueryReport 
implements NotificationE
                         "Composite data type for query statistics", 
QueryStats.getFieldNames(),
                         QueryStats.getFieldDescriptions(), 
QueryStats.getFieldTypes());
             } catch (OpenDataException x) {
-                log.warn("Unable to initialize composite data type for JMX 
stats and notifications.", x);
+                
log.warn(MessageFactory.get("slowQueryReportJmx.unableToInitCompositeType"), x);
             }
         }
         return SLOW_QUERY_TYPE;
@@ -569,7 +570,7 @@ public class SlowQueryReportJmx extends SlowQueryReport 
implements NotificationE
             notifiers.remove(poolName);
             registrationFailuresLogged.remove(poolName);
         } catch (MalformedObjectNameException | RuntimeOperationsException e) {
-            log.warn("Jmx deregistration failed.", e);
+            
log.warn(MessageFactory.get("slowQueryReportJmx.jmxDeregistrationFailed"), e);
         }
 
     }
@@ -679,25 +680,19 @@ public class SlowQueryReportJmx extends SlowQueryReport 
implements NotificationE
                                 // Retryable: the name may be held by a stale
                                 // or foreign MBean that a later borrow finds
                                 // gone
-                                log.warn("Jmx registration of [" + oname +
-                                        "] failed: the name is already in use 
by another MBean." +
-                                        " The registration is retried by later 
borrows;" +
-                                        " repeated failures are logged at 
debug level.", x);
+                                
log.warn(MessageFactory.get("slowQueryReportJmx.jmxRegistrationInUse", oname), 
x);
                             } else {
-                                log.error("Jmx registration of [" + oname +
-                                        "] failed, no JMX data will be exposed 
for the query stats" +
-                                        " until a registration succeeds. The 
registration is" +
-                                        " retried by later borrows; repeated 
failures are" + " logged at debug level.",
-                                        x);
+                                
log.error(MessageFactory.get("slowQueryReportJmx.jmxRegistrationFailed", 
oname), x);
                             }
                         }
                     }
                 }
             } else {
-                log.warn(SlowQueryReport.class.getName() + "- No JMX support, 
composite type was not found.");
+                log.warn(MessageFactory.get("slowQueryReportJmx.noJmxSupport",
+                        SlowQueryReport.class.getName()));
             }
         } catch (MalformedObjectNameException | RuntimeOperationsException e) {
-            log.error("Jmx registration failed, no JMX data will be exposed 
for the query stats.", e);
+            
log.error(MessageFactory.get("slowQueryReportJmx.jmxRegistrationFailedNoData"), 
e);
         }
     }
 
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
index c5dd376cf6..04787b3132 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
@@ -38,6 +38,7 @@ import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
 import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
 import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Interceptor that caches {@code PreparedStatement} and/or {@code 
CallableStatement} instances on a connection.
@@ -282,7 +283,7 @@ public class StatementCache extends 
StatementDecoratorInterceptor implements Sta
         }
         CacheKey key = createCacheKey(method, args);
         if (key == null) {
-            throw new IllegalArgumentException("Null key");
+            throw new 
IllegalArgumentException(MessageFactory.get("statementCache.nullKey"));
         }
         return cache.get(key);
     }
@@ -437,7 +438,7 @@ public class StatementCache extends 
StatementDecoratorInterceptor implements Sta
         PooledConnection pCon = this.pcon;
         if (pCon == null) {
             if (log.isWarnEnabled()) {
-                log.warn("Connection has already been closed or abandoned");
+                
log.warn(MessageFactory.get("statementCache.connectionClosedOrAbandoned"));
             }
             return null;
         }
@@ -649,7 +650,7 @@ public class StatementCache extends 
StatementDecoratorInterceptor implements Sta
             try {
                 super.closeInvoked();
             } catch (SQLException x) {
-                log.warn("Unable to close statement [" + getSql() + "] that 
the statement cache is discarding.", x);
+                
log.warn(MessageFactory.get("statementCache.unableToCloseDiscardedStatement", 
getSql()), x);
             }
         }
 
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
index 2e7e481d7f..86956eddac 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
@@ -29,6 +29,7 @@ import java.sql.Statement;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Implementation of <b>JdbcInterceptor</b> that proxies resultSets and 
statements.
@@ -403,7 +404,7 @@ public class StatementDecoratorInterceptor extends 
AbstractCreateStatementInterc
             }
             // If we are calling anything else, bail out
             if (gone) {
-                throw new SQLException("Statement closed.");
+                throw new 
SQLException(MessageFactory.get("statementDecoratorInterceptor.statementClosed"));
             }
             if (compare(GETCONNECTION_VAL, method)) {
                 // The handle of the current borrow, as bound by
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java
index dd4bafa1a7..4223050e9e 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java
@@ -32,6 +32,7 @@ import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.jdbc.pool.ConnectionPool;
 import org.apache.tomcat.jdbc.pool.PoolProperties;
 import org.apache.tomcat.jdbc.pool.PooledConnection;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Keeps track of statements associated with a connection and invokes close 
upon {@link java.sql.Connection#close()}
@@ -113,7 +114,7 @@ public class StatementFinalizer extends 
AbstractCreateStatementInterceptor {
             // The statement is still tracked and will be closed when the
             // connection is returned, but closing it early will no longer
             // forget it before then.
-            log.warn("Unable to create statement tracking proxy.", x);
+            
log.warn(MessageFactory.get("statementFinalizer.unableToCreateTrackingProxy"), 
x);
             return tracked;
         }
     }
@@ -216,7 +217,7 @@ public class StatementFinalizer extends 
AbstractCreateStatementInterceptor {
                 }
             }
             if (logCreationStack && shallClose) {
-                log.warn("Statement created, but was not closed at:", 
allocationStack);
+                
log.warn(MessageFactory.get("statementFinalizer.statementNotClosed"), 
allocationStack);
             }
         }
     }
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
index 60f6c8c8f5..a8ee86ae21 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
@@ -37,6 +37,7 @@ import org.apache.tomcat.jdbc.pool.PoolConfiguration;
 import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition;
 import org.apache.tomcat.jdbc.pool.PoolUtilities;
 import org.apache.tomcat.jdbc.pool.Validator;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * JMX MBean wrapper for a {@link org.apache.tomcat.jdbc.pool.ConnectionPool}. 
Provides management and monitoring
@@ -260,8 +261,8 @@ public class ConnectionPool extends 
NotificationBroadcasterSupport implements Co
             // The delivery queue is full; dropping the notification is the
             // point of the bound, and must not fail the pool operation that
             // reported the condition
-            log.warn("Dropping notification of type [" + type + "] for pool [" 
+ getNotificationSource() +
-                    "]: the notification delivery queue (" + 
NOTIFY_QUEUE_CAPACITY + " deliveries) is full");
+            
log.warn(MessageFactory.get("jmxConnectionPool.droppingNotification",
+                    type, getNotificationSource(), NOTIFY_QUEUE_CAPACITY));
             return false;
         } catch (Exception x) {
             if (log.isDebugEnabled()) {
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/JmxUtil.java 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/JmxUtil.java
index c554c7652b..145599ee7f 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/JmxUtil.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/JmxUtil.java
@@ -24,6 +24,7 @@ import javax.management.ObjectName;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jdbc.pool.util.MessageFactory;
 
 /**
  * Utility class for JMX registration of JDBC pool components.
@@ -62,7 +63,7 @@ public class JmxUtil {
                 oname = name;
             }
         } catch (Exception e) {
-            log.error("Jmx registration failed.", e);
+            log.error(MessageFactory.get("jmxUtil.jmxRegistrationFailed"), e);
         }
         return oname;
     }
@@ -79,7 +80,7 @@ public class JmxUtil {
         try {
             ManagementFactory.getPlatformMBeanServer().unregisterMBean(oname);
         } catch (Exception e) {
-            log.error("Jmx unregistration failed.", e);
+            log.error(MessageFactory.get("jmxUtil.jmxUnregistrationFailed"), 
e);
         }
     }
 
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/LocalStrings.properties
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/LocalStrings.properties
new file mode 100644
index 0000000000..906f00a192
--- /dev/null
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/LocalStrings.properties
@@ -0,0 +1,199 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+abstractCreateStatementInterceptor.constructorCacheIndexPopulated=Constructor 
cache index {0} is already populated for {1}, but {2} was requested. Every use 
of an index must pass an interface the cached proxy implements.
+abstractCreateStatementInterceptor.unableToCreateConstructorCacheEntry=Unable 
to create the constructor cache entry for index {0} ({1}).
+
+abstractQueryReport.statementClosed=Statement closed.
+abstractQueryReport.unableToCreateStatementProxy=Unable to create statement 
proxy for slow query report.
+abstractQueryReport.unableToProcessFailedQuery=Unable to process failed query 
for {0}
+abstractQueryReport.unableToProcessQueryReport=Unable to process query report 
for {0}
+abstractQueryReport.unableToProcessSlowQuery=Unable to process slow query for 
{0}
+
+classLoaderUtil.classLoaderNull=Classloader is null
+classLoaderUtil.classLoadFailed=Unable to load class: {0} from {1}
+
+connectionPool.checkAbandonedFailed=checkAbandoned failed, it will be retried.
+connectionPool.checkIdleFailed=checkIdle failed, it will be retried.
+connectionPool.connectionAbandoned=Connection has been abandoned {0}:{1}
+connectionPool.connectionSuspect=Connection has been marked suspect, possibly 
abandoned {0}[{1} ms.]:{2}
+connectionPool.evicterIntervalTooLow=Database connection pool evicter thread 
interval is set to lower than 1 second.
+connectionPool.evicterIntervalZero=Database connection pool evicter thread 
interval is set to 0, defaulting to 30 seconds
+connectionPool.failedToAddNewConnection=Failed to add a new connection to the 
pool after releasing a connection when at least one thread was waiting for a 
connection.
+connectionPool.failedToReconnectExpired=Failed to re-connect connection [{0}] 
that expired because of maxAge
+connectionPool.failedToValidateNewConnection=Failed to validate a newly 
established connection.
+connectionPool.interceptorDisconnectedFailed=Interceptor [{0}] threw an 
exception from disconnected(); continuing with the rest of the chain.
+connectionPool.interceptorResetFailed=Interceptor [{0}] threw an exception 
from reset(); continuing with the rest of the chain.
+connectionPool.initialSizeTooLarge=initialSize is larger than maxActive, 
setting initialSize to: {0}
+connectionPool.maxActiveTooSmall=maxActive is smaller than 1, setting 
maxActive to: {0}
+connectionPool.maxIdleTooLarge=maxIdle is larger than maxActive, setting 
maxIdle to: {0}
+connectionPool.maxIdleTooSmall=maxIdle is smaller than minIdle, setting 
maxIdle to: {0}
+connectionPool.minIdleTooLarge=minIdle is larger than maxActive, setting 
minIdle to: {0}
+connectionPool.poolClosed=Connection pool closed.
+connectionPool.poolEmptyNoWait=[{0}] NoWait: Pool empty. Unable to fetch a 
connection, none available[{1} in use].
+connectionPool.poolEmptyTimeout=[{0}] Timeout: Pool empty. Unable to fetch a 
connection in {1} seconds, none available[size:{2}; busy:{3}; idle:{4}; 
lastwait:{5}].
+connectionPool.poolMisconfigured=Connection pool is misconfigured, doesn''t 
support async retrieval. Set the ''fair'' property to ''true''
+connectionPool.poolWaitInterrupted=Pool wait interrupted.
+connectionPool.requestHintRefused=The request boundary hint [{0}] was refused 
by the driver
+connectionPool.statementCacheBeforeQueryTimeout=The [StatementCache] 
interceptor is configured before [QueryTimeoutInterceptor]. A statement cache 
hit is answered by the cache without invoking the timeout interceptor, and a 
statement recycled into the cache carries no timeout, so cached statements run 
without a query timeout. List QueryTimeoutInterceptor before StatementCache in 
the jdbcInterceptors configuration.
+connectionPool.suspectConnectionReturned=Connection({0}) that has been marked 
suspect was returned. The processing time is {1} ms.
+connectionPool.testAllIdleFailed=testAllIdle failed, it will be retried.
+connectionPool.timeBetweenEvictionRunsTooLarge=timeBetweenEvictionRunsMillis 
is larger than maxAge, setting timeBetweenEvictionRunsMillis to: {0}
+connectionPool.unableToCancelConnectionFuture=Unable to cancel 
ConnectionFuture.
+connectionPool.unableToCheckClosed=Unable to check if connection is closed
+connectionPool.unableToClosePooledConnection=Unable to close pooled connection.
+connectionPool.unableToCreateInitialConnections=Unable to create initial 
connections of pool.
+connectionPool.unableToInformInterceptorOfPoolStart=Unable to inform 
interceptor of pool start.
+connectionPool.unableToInstantiateInterceptorChain=Unable to instantiate 
interceptor chain.
+connectionPool.unableToRegisterMBean=Unable to register the pooled connection 
MBean.
+connectionPool.unableToResetInterceptor=Unable to reset interceptor [{0}].
+connectionPool.unableToReturnConnection=Unable to return connection [{0}] to 
the pool, closing it.
+connectionPool.unableToSendRequestHint=Unable to send the request boundary 
hint [{0}]
+connectionPool.unableToStartJmx=Unable to start JMX integration for connection 
pool. Instance[{0}] can''t be monitored.
+connectionPool.unableToTestIdleConnection=Unable to test idle connection 
[{0}], closing it.
+connectionPool.unableToTerminateTransaction=Unable to terminate transaction, 
connection will be closed.
+connectionPool.validationQueryFailed=Validation Query Failed, enable 
logValidationErrors for more details.
+
+connectionPool.asyncBorrowConnectionReleased=The pooled connection served to 
this asynchronous borrow was released before it could be set up; no connection 
was borrowed, borrow the pool again.
+
+connectionState.unableToRestoreAutoCommit=Unable to restore the configured 
auto-commit state {0} on the connection.
+connectionState.unableToRestoreCatalog=Unable to restore the configured 
default catalog {0} on the connection.
+connectionState.unableToRestoreReadOnly=Unable to restore the configured 
read-only state {0} on the connection.
+connectionState.unableToResetTransactionIsolation=Unable to reset transaction 
isolation state to connection. The configured level {0} will not be retried on 
this session; it keeps its current isolation level.
+connectionState.resetStateToNull=Reset state to null as an exception occurred 
while calling method[{0}].
+connectionState.unableToTerminatePendingTransaction=Unable to check for, or 
terminate, the pending transaction before restoring the configured connection 
state.
+
+dataSource.mbeanNotRegistered=JDBC pool MBean [{0}] not registered: the pool 
has not been created yet or has already been closed.
+dataSource.unableToCreateObjectName=Unable to create object name for JDBC pool.
+dataSource.unableToRegisterMBean=Unable to register JDBC pool MBean.
+
+dataSourceFactory.cannotSetDataSourceString=Can''t set dataSource property as 
a string "{0}". Either carry the javax.sql.DataSource object itself under the 
dataSource attribute of the Reference, or use the dataSourceJNDI attribute to 
name a data source bound in JNDI.
+dataSourceFactory.invalidBoolean=Invalid boolean value ''{0}'' for property {1}
+dataSourceFactory.invalidClassName={0} is not a valid class name/type for this 
JNDI factory.
+dataSourceFactory.invalidProperty={0} is not a valid property, it will be 
ignored.
+dataSourceFactory.invalidSetting={0} is not a valid setting, it will have no 
effect.
+dataSourceFactory.jndiContextNull=dataSourceJNDI property is configured, but 
local JNDI context is null.
+dataSourceFactory.jndiLookupFailed=Unable to create the connection pool: the 
name [{0}] configured through the ''dataSourceJNDI'' attribute was not found in 
the local JNDI context nor in the InitialContext. The pool is built around the 
data source that name refers to; check that the name is correct and that the 
data source is bound and visible to the application before the pool is created.
+dataSourceFactory.jndiNameNotFound=The name "{0}" cannot be found in the 
InitialContext.
+
+dataSourceProxy.alternateUsernameIgnored=alternateUsernameAllowed is set to 
false so the provided user name [{0}] and associated password have been ignored
+dataSourceProxy.connectionNotXA=Connection from pool does not implement 
javax.sql.XAConnection
+dataSourceProxy.dataSourceClosed=DataSource has been closed.
+dataSourceProxy.errorClosingPool=Error during connection pool closure.
+dataSourceProxy.errorCreatingPool=Error during connection pool creation.
+dataSourceProxy.notAWrapper=Not a wrapper of {0}
+dataSourceProxy.poolConfigurationNull=PoolConfiguration cannot be null.
+dataSourceProxy.unableToParseConnectionProperties=Unable to parse connection 
properties.
+dataSourceProxy.unableToPurgePool=Unable to purge pool.
+
+disposableConnectionFacade.pooledConnectionClosed=PooledConnection has already 
been closed.
+
+fairBlockingQueue.itemFutureAbandoned=The waiter of this future timed out on 
an earlier get() call and was deregistered from the queue; the future can no 
longer receive an item
+fairBlockingQueue.itemFutureInstantiationError=ItemFuture incorrectly 
instantiated. Bug in the code?
+fairBlockingQueue.unsupportedClear=void clear()
+fairBlockingQueue.unsupportedContainsAll=boolean containsAll(Collection<?> c)
+fairBlockingQueue.unsupportedDrainTo=int drainTo(Collection<? super E> c, int 
maxElements)
+fairBlockingQueue.unsupportedElement=E element()
+fairBlockingQueue.unsupportedPeek=E peek()
+fairBlockingQueue.unsupportedRemove=E remove()
+fairBlockingQueue.unsupportedRemoveAll=boolean removeAll(Collection<?> c)
+fairBlockingQueue.unsupportedRetainAll=boolean retainAll(Collection<?> c)
+fairBlockingQueue.unsupportedToArray=Object[] toArray()
+fairBlockingQueue.unsupportedToArrayT=<T> T[] toArray(T[] a)
+
+genericNamingResourcesFactory.classNotFound=Class [{0}] of reference [{1}] was 
not found
+genericNamingResourcesFactory.constructorThrew=The constructor of class [{0}] 
of reference [{1}] threw an exception and the object cannot be created
+genericNamingResourcesFactory.illegalAccessExceptionSettingProperty=IllegalAccessException
 setting property [{0}] with value [{1}] on [{2}]
+genericNamingResourcesFactory.noResourceType=No resource type (the "type" 
attribute) is defined for reference [{0}]; GenericNamingResourcesFactory needs 
the class of the object to create.
+genericNamingResourcesFactory.noUsableConstructor=Class [{0}] of reference 
[{1}] has no usable public no-argument constructor and cannot be created by 
this factory
+genericNamingResourcesFactory.propertyNotApplied=Property [{0}] was not 
applied to [{1}]: no usable setter found, or the configured value could not be 
applied.
+genericNamingResourcesFactory.securityExceptionSettingProperty=SecurityException
 setting property [{0}] with value [{1}] on [{2}]
+genericNamingResourcesFactory.setterThrew=Setter for property [{0}] with value 
[{1}] threw an exception on [{2}]
+genericNamingResourcesFactory.unableToSetProperty=Unable to set property [{0}] 
with value [{1}] on [{2}]
+
+jmxConnectionPool.droppingNotification=Dropping notification of type [{0}] for 
pool [{1}]: the notification delivery queue ({2} deliveries) is full
+
+jmxUtil.jmxRegistrationFailed=Jmx registration failed.
+jmxUtil.jmxUnregistrationFailed=Jmx unregistration failed.
+
+poolProperties.invalidInterceptorProperty=Invalid interceptor property 
''{0}'', missing ''='', in jdbcInterceptors value [{1}]
+poolProperties.layeredPoolsNotAllowed=Layered pools are not allowed.
+poolProperties.validatorCannotInstantiate=An object of class {0} cannot be 
instantiated. Make sure that it includes an implicit or explicit no-arg 
constructor.
+poolProperties.validatorClassNotFound=The class {0} cannot be found.
+poolProperties.validatorInaccessible=The class {0} or its no-arg constructor 
are inaccessible.
+poolProperties.validatorNotValidator=The class {0} does not implement the 
Validator interface.
+
+pooledConnection.cannotReestablish=A connection once released, can''t be 
reestablished.
+pooledConnection.commitFailed=Failed to commit transaction
+pooledConnection.connectionPoolDataSourceNullConnection=ConnectionPoolDataSource
 {0} returned a null connection for the pooled connection handle
+pooledConnection.customValidationFailed=Custom validation through {0} failed.
+pooledConnection.customValidationThrew=Custom validation through {0} threw an 
exception.
+pooledConnection.dataSourceNullConnection=DataSource {0} returned a null 
connection
+pooledConnection.discardedStateChange=Unable to change the state once the 
connection has been discarded
+pooledConnection.disconnected=Connection has been disconnected.
+pooledConnection.driverReturnedNull=Driver:{0} returned null for URL:{1}
+pooledConnection.interceptorDisconnectedFailed=Interceptor threw an exception 
from the disconnected() event; ignoring it so that the physical connection can 
be closed.
+pooledConnection.interceptorResetFailed=Interceptor [{0}] threw an exception 
from reset(); continuing with the rest of the chain.
+pooledConnection.isValidFailed=isValid() failed.
+pooledConnection.isValidReturnedFalse=isValid() returned false.
+pooledConnection.rollbackFailed=Failed to rollback transaction
+pooledConnection.sqlValidationError=SQL Validation error
+pooledConnection.unableToClearWarnings=Unable to clear warnings on the 
connection.
+pooledConnection.unknownDataSource=DataSource is of unknown class:{0}
+pooledConnection.xaDataSourceNullConnection=XADataSource {0} returned a null 
connection for the XA connection handle
+
+proxyConnection.connectionClosed=Connection has already been closed.
+proxyConnection.notAWrapper=Not a wrapper of {0}
+proxyConnection.notXAConnection=The pooled connection is not an XA connection; 
method [{0}] cannot be dispatched.
+
+queryTimeoutInterceptor.invalidQueryTimeout=[QueryTimeoutInterceptor] The 
configured queryTimeout [{0}] is not an integer number of seconds and is not 
applied: statements created through this configuration keep the default 
{1}-second query timeout. Correct the value in the jdbcInterceptors 
configuration to set the timeout that was meant to be configured.
+queryTimeoutInterceptor.queryTimeoutRefused=[QueryTimeoutInterceptor] Unable 
to set the query timeout of {0} seconds on a statement of this connection: {1}. 
The driver does not apply the timeout, so statements created on this physical 
connection run without a query timeout. Refusals are not logged again on this 
connection.
+
+resetAbandonedTimer.resultSetClosed=ResultSet closed.
+resetAbandonedTimer.statementClosed=Statement closed.
+resetAbandonedTimer.unableToCreateTimerResultSetProxy=Unable to create timer 
result set proxy for {0}.
+resetAbandonedTimer.unableToCreateTimerStatementProxy=Unable to create timer 
statement proxy for {0}.
+
+slowQueryReport.connectionClosedOrAbandoned=Connection has already been closed 
or abandoned
+slowQueryReport.failedQueryReport=Failed Query Report SQL={0}; time={1} ms;
+slowQueryReport.poolNameInUse=Connection pool name [{0}] is already in use by 
another live pool; SlowQueryReport statistics are keyed by pool name, so both 
pools report into the same statistics map.
+slowQueryReport.slowQueryReport=Slow Query Report SQL={0}; time={1} ms;
+
+slowQueryReportJmx.jmxDeregistrationFailed=Jmx deregistration failed.
+slowQueryReportJmx.jmxRegistrationFailed=Jmx registration of [{0}] failed, no 
JMX data will be exposed for the query stats until a registration succeeds. The 
registration is retried by later borrows; repeated failures are logged at debug 
level.
+slowQueryReportJmx.jmxRegistrationFailedNoData=Jmx registration failed, no JMX 
data will be exposed for the query stats.
+slowQueryReportJmx.jmxRegistrationInUse=Jmx registration of [{0}] failed: the 
name is already in use by another MBean. The registration is retried by later 
borrows; repeated failures are logged at debug level.
+slowQueryReportJmx.noJmxSupport={0}- No JMX support, composite type was not 
found.
+slowQueryReportJmx.unableToInitCompositeType=Unable to initialize composite 
data type for JMX stats and notifications.
+
+statementCache.connectionClosedOrAbandoned=Connection has already been closed 
or abandoned
+statementCache.nullKey=Null key
+statementCache.unableToCloseDiscardedStatement=Unable to close statement [{0}] 
that the statement cache is discarding.
+
+statementDecoratorInterceptor.statementClosed=Statement closed.
+
+statementFacade.resultSetClosed=ResultSet closed.
+statementFacade.resultSetClosedConnectionReturned=ResultSet closed. The pooled 
connection this result set was read from has been returned to the pool.
+statementFacade.statementClosed=Statement closed.
+statementFacade.statementClosedConnectionReturned=Statement closed. The pooled 
connection this statement was created from has been returned to the pool.
+
+statementFinalizer.statementNotClosed=Statement created, but was not closed at:
+statementFinalizer.unableToCreateTrackingProxy=Unable to create statement 
tracking proxy.
+
+trapException.uncaughtUnderlyingException=Uncaught underlying exception.
+
+xaDataSource.dataSourceNotXA=The connection pool [{0}] cannot provide XA 
connections: the data source configured through the ''dataSource'' or 
''dataSourceJNDI'' attribute ({1}) does not implement javax.sql.XADataSource.
+xaDataSource.jndiNotResolved=The connection pool [{0}] cannot provide XA 
connections: the name "{1}" set through the ''dataSourceJNDI'' attribute has 
not been resolved into a data source; the name is only looked up when the pool 
is created through the DataSourceFactory. Set the ''dataSource'' attribute to a 
javax.sql.XADataSource provided by the JDBC driver.
+xaDataSource.noDataSource=The connection pool [{0}] cannot provide XA 
connections: neither the ''dataSource'' nor the ''dataSourceJNDI'' attribute is 
set, so only plain JDBC connections from a JDBC driver and URL can be pooled. 
Set the ''dataSource'' or ''dataSourceJNDI'' attribute to a 
javax.sql.XADataSource provided by the JDBC driver.
diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/MessageFactory.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/MessageFactory.java
new file mode 100644
index 0000000000..90520db49d
--- /dev/null
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/MessageFactory.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.jdbc.pool.util;
+
+import java.text.Format;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Factory for creating localized JDBC pool messages.
+ */
+public final class MessageFactory {
+
+    private static final ResourceBundle DEFAULT_BUNDLE =
+            
ResourceBundle.getBundle("org.apache.tomcat.jdbc.pool.util.LocalStrings");
+
+    private static final MessageFactory DEFAULT_MESSAGE_FACTORY = new 
MessageFactory(DEFAULT_BUNDLE);
+
+    /**
+     * Get a localized message.
+     *
+     * @param key the message key
+     *
+     * @return the message
+     */
+    public static String get(final String key) {
+        return DEFAULT_MESSAGE_FACTORY.getInternal(key);
+    }
+
+    /**
+     * Get a localized message with arguments.
+     *
+     * @param key  the message key
+     * @param args the arguments
+     *
+     * @return the message
+     */
+    public static String get(final String key, final Object... args) {
+        return DEFAULT_MESSAGE_FACTORY.getInternal(key, args);
+    }
+
+    private final ResourceBundle bundle;
+
+    /**
+     * Constructor.
+     *
+     * @param bundle the resource bundle
+     */
+    public MessageFactory(ResourceBundle bundle) {
+        this.bundle = bundle;
+    }
+
+    /**
+     * Get a message from the bundle.
+     *
+     * @param key the message key
+     *
+     * @return the message
+     */
+    protected String getInternal(final String key) {
+        try {
+            return bundle.getString(key);
+        } catch (MissingResourceException e) {
+            return key;
+        }
+    }
+
+    /**
+     * Get a message from the bundle with arguments.
+     *
+     * @param key  the message key
+     * @param args the arguments
+     *
+     * @return the message
+     */
+    protected String getInternal(final String key, final Object... args) {
+        String value = getInternal(key);
+
+        MessageFormat mf = new MessageFormat(value);
+        Format[] formats = null;
+
+        // Unless an argument has been explicitly configured to use a number
+        // format, convert all Number arguments to String else MessageFormat 
may
+        // try to format them in unexpected ways.
+        if (args != null) {
+            for (int i = 0; i < args.length; i++) {
+                if (args[i] instanceof Number) {
+                    if (formats == null) {
+                        formats = mf.getFormatsByArgumentIndex();
+                    }
+                    if (i < formats.length && !(formats[i] instanceof 
NumberFormat)) {
+                        args[i] = args[i].toString();
+                    }
+                }
+            }
+        }
+
+        return mf.format(args, new StringBuffer(), null).toString();
+    }
+}
diff --git 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportLogging.java
 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportLogging.java
index 8b0b6c4595..d81d055d5b 100644
--- 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportLogging.java
+++ 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportLogging.java
@@ -232,7 +232,6 @@ class TestSlowQueryReportLogging extends JdbcPoolBaseTest {
     }
 
     /**
-
      * Captures what {@link SlowQueryReport} logs, following the handler
      * idiom of the pool's other diagnostic tests. The canary record -
      * written through the very JULI logger the interceptor logs through,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to