You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ps...@apache.org on 2015/02/16 21:18:39 UTC

svn commit: r1660195 - in /commons/proper/dbcp/trunk/src: changes/ main/java/org/apache/commons/dbcp2/ test/java/org/apache/commons/dbcp2/

Author: psteitz
Date: Mon Feb 16 20:18:38 2015
New Revision: 1660195

URL: http://svn.apache.org/r1660195
Log:
Added property name verification to BasicDataSourceFactory. References including
obsolete or unrecognized properties now generate log messages.
JIRA: DBCP-435
Thanks to Denixx Baykin.

Modified:
    commons/proper/dbcp/trunk/src/changes/changes.xml
    commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
    commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/StackMessageLog.java
    commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java

Modified: commons/proper/dbcp/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/changes/changes.xml?rev=1660195&r1=1660194&r2=1660195&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/changes/changes.xml (original)
+++ commons/proper/dbcp/trunk/src/changes/changes.xml Mon Feb 16 20:18:38 2015
@@ -114,6 +114,10 @@ The <action> type attribute can be add,u
       <action dev="psteitz" type="update">
         Eliminated synchronization in BasicDataSource getNumActive, getNumIdle methods.
       </action>
+      <action issue="DBCP-435" type="update" due-to="Denixx Baykin">
+        Added property name verification to BasicDataSourceFactory. References including
+        obsolete or unrecognized properties now generate log messages.
+      </action>
     </release>
     <release version="2.0.1" date="24 May 2014" description="This is a bug fix release.">
       <action dev="markt" type="fix">

Modified: commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java?rev=1660195&r1=1660194&r2=1660195&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java (original)
+++ commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java Mon Feb 16 20:18:38 2015
@@ -17,13 +17,22 @@
 
 package org.apache.commons.dbcp2;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+
 import java.io.ByteArrayInputStream;
 import java.nio.charset.StandardCharsets;
 import java.sql.Connection;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
 
@@ -52,6 +61,8 @@ import javax.naming.spi.ObjectFactory;
  */
 public class BasicDataSourceFactory implements ObjectFactory {
 
+    private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);
+
     private static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
     private static final String PROP_DEFAULTREADONLY = "defaultReadOnly";
     private static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
@@ -105,6 +116,24 @@ public class BasicDataSourceFactory impl
      */
     private static final String PROP_DISCONNECTION_SQL_CODES = "disconnectionSqlCodes";
 
+    /*
+     * Block with obsolete properties from DBCP 1.x.
+     * Warn users that these are ignored and they should use the 2.x properties.
+     */
+    private static final String NUPROP_MAXACTIVE = "maxActive";
+    private static final String NUPROP_REMOVEABANDONED = "removeAbandoned";
+    private static final String NUPROP_MAXWAIT = "maxWait";
+
+    /*
+     * Block with properties expected in a DataSource
+     * This props will not be listed as ignored - we know that they may appear in Resource,
+     * and not listing them as ignored.
+     */
+    private static final String SILENTPROP_FACTORY = "factory";
+    private static final String SILENTPROP_SCOPE = "scope";
+    private static final String SILENTPROP_SINGLETON = "singleton";
+    private static final String SILENTPROP_AUTH = "auth";
+
     private static final String[] ALL_PROPERTIES = {
         PROP_DEFAULTAUTOCOMMIT,
         PROP_DEFAULTREADONLY,
@@ -150,6 +179,46 @@ public class BasicDataSourceFactory impl
         PROP_DISCONNECTION_SQL_CODES
     };
 
+    /**
+     * Obsolete properties from DBCP 1.x. with warning strings suggesting
+     * new properties. LinkedHashMap will guarantee that properties will be listed
+     * to output in order of insertion into map.
+     */
+    private static final Map<String, String> NUPROP_WARNTEXT = new LinkedHashMap<>();
+
+    static {
+        NUPROP_WARNTEXT.put(
+                NUPROP_MAXACTIVE,
+                "Property " + NUPROP_MAXACTIVE + " is not used in DBCP2, use " + PROP_MAXTOTAL + " instead. "
+                        + PROP_MAXTOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL+".");
+        NUPROP_WARNTEXT.put(
+                NUPROP_REMOVEABANDONED,
+                "Property " + NUPROP_REMOVEABANDONED + " is not used in DBCP2,"
+                        + " use one or both of "
+                        + PROP_REMOVEABANDONEDONBORROW + " or " + PROP_REMOVEABANDONEDONMAINTENANCE + " instead. "
+                        + "Both have default value set to false.");
+        NUPROP_WARNTEXT.put(
+                NUPROP_MAXWAIT,
+                "Property " + NUPROP_MAXWAIT + " is not used in DBCP2"
+                        + " , use " + PROP_MAXWAITMILLIS + " instead. "
+                        + PROP_MAXWAITMILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS+".");
+    }
+
+    /**
+     * Silent Properties.
+     * These properties will not be listed as ignored - we know that they may appear in JDBC Resource references,
+     * and we will not list them as ignored.
+     */
+    private static final List<String> SILENT_PROPERTIES = new ArrayList<>();
+
+    static {
+        SILENT_PROPERTIES.add(SILENTPROP_FACTORY);
+        SILENT_PROPERTIES.add(SILENTPROP_SCOPE);
+        SILENT_PROPERTIES.add(SILENTPROP_SINGLETON);
+        SILENT_PROPERTIES.add(SILENTPROP_AUTH);
+
+    }
+
     // -------------------------------------------------- ObjectFactory Methods
 
     /**
@@ -181,6 +250,17 @@ public class BasicDataSourceFactory impl
             return null;
         }
 
+        // Check property names and log warnings about obsolete and / or unknown properties
+        final List<String> warnings = new ArrayList<String>();
+        final List<String> infoMessages = new ArrayList<String>();
+        validatePropertyNames(ref, name, warnings, infoMessages);
+        for (String warning : warnings) {
+            log.warn(warning);
+        }
+        for (String infoMessage : infoMessages) {
+            log.info(infoMessage);
+        }
+
         Properties properties = new Properties();
         for (String propertyName : ALL_PROPERTIES) {
             RefAddr ra = ref.get(propertyName);
@@ -194,6 +274,58 @@ public class BasicDataSourceFactory impl
     }
 
     /**
+     * Collects warnings and info messages.  Warnings are generated when an obsolete
+     * property is set.  Unknown properties generate info messages.
+     *
+     * @param ref Reference to check properties of
+     * @param name Name provided to getObject
+     * @param warnings container for warning messages
+     * @param infoMessasges container for info messages
+     */
+    private void validatePropertyNames(Reference ref, Name name, List<String> warnings,
+                                      List<String> infoMessages) {
+        final List<String> allPropsAsList = Arrays.asList(ALL_PROPERTIES);
+        final String nameString = name != null ? "Name = " + name.toString() + " " : ""; 
+        if (NUPROP_WARNTEXT!=null && !NUPROP_WARNTEXT.keySet().isEmpty()) {
+            for (String propertyName : NUPROP_WARNTEXT.keySet()) {
+                final RefAddr ra = ref.get(propertyName);
+                if (ra != null && !allPropsAsList.contains(ra.getType())) {
+                    final StringBuilder stringBuilder = new StringBuilder(nameString);
+                    final String propertyValue = ra.getContent().toString();
+                    stringBuilder.append(NUPROP_WARNTEXT.get(propertyName))
+                            .append(" You have set value of \"")
+                            .append(propertyValue)
+                            .append("\" for \"")
+                            .append(propertyName)
+                            .append("\" property, which is being ignored.");
+                    warnings.add(stringBuilder.toString());
+                }
+            }
+        }
+
+        final Enumeration<RefAddr> allRefAddrs = ref.getAll();
+        while (allRefAddrs.hasMoreElements()) {
+            final RefAddr ra = allRefAddrs.nextElement();
+            final String propertyName = ra.getType();
+            // If property name is not in the properties list, we haven't warned on it
+            // and it is not in the "silent" list, tell user we are ignoring it.
+            if (!(allPropsAsList.contains(propertyName)
+                    || NUPROP_WARNTEXT.keySet().contains(propertyName)
+                    || SILENT_PROPERTIES.contains(propertyName))) {
+                final String propertyValue = ra.getContent().toString();
+                final StringBuilder stringBuilder = new StringBuilder(nameString);
+                stringBuilder.append("Ignoring unknown property: ")
+                        .append("value of \"")
+                        .append(propertyValue)
+                        .append("\" for \"")
+                        .append(propertyName)
+                        .append("\" property");
+                infoMessages.add(stringBuilder.toString());
+            }
+        }
+    }
+
+    /**
      * Creates and configures a {@link BasicDataSource} instance based on the
      * given properties.
      *

Modified: commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/StackMessageLog.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/StackMessageLog.java?rev=1660195&r1=1660194&r2=1660195&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/StackMessageLog.java (original)
+++ commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/StackMessageLog.java Mon Feb 16 20:18:38 2015
@@ -17,7 +17,10 @@
 
 package org.apache.commons.dbcp2;
 
+import java.util.ArrayList;
 import java.util.EmptyStackException;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Stack;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -30,7 +33,7 @@ import org.apache.commons.logging.impl.S
  *
  * @version $Id$
  */
-public class StackMessageLog extends SimpleLog  {
+public class StackMessageLog extends SimpleLog {
     
     private static final long serialVersionUID = 1L;
     private static Stack<String> messageStack = new Stack<String>();
@@ -80,6 +83,18 @@ public class StackMessageLog extends Sim
         }
         return ret;
     }
+    
+    /**
+     * Note: iterator is fail-fast, lock the stack first.
+     */
+    public static List<String> getAll() {
+        final Iterator<String> iterator = messageStack.iterator();
+        final List<String> messages = new ArrayList<String>();
+        while (iterator.hasNext()) {
+            messages.add(iterator.next());
+        }
+        return messages;
+    }
 
     public static void clear() {
         lock.lock();

Modified: commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java?rev=1660195&r1=1660194&r2=1660195&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java (original)
+++ commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java Mon Feb 16 20:18:38 2015
@@ -22,7 +22,10 @@ import static org.junit.Assert.assertNot
 import static org.junit.Assert.assertTrue;
 
 import java.sql.Connection;
+import java.util.List;
 import java.util.Properties;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
 
 import org.junit.Test;
 
@@ -118,4 +121,31 @@ public class TestBasicDataSourceFactory
         assertTrue(ds.getDisconnectionSqlCodes().contains("XXX"));
         assertTrue(ds.getDisconnectionSqlCodes().contains("YYY"));
     }
+    
+    @Test
+    public void testValidateProperties() throws Exception {
+        try {
+            StackMessageLog.lock();
+            final Reference ref = new Reference("javax.sql.DataSource",
+                                          BasicDataSourceFactory.class.getName(), null);
+            ref.add(new StringRefAddr("foo", "bar"));     // Unknown
+            ref.add(new StringRefAddr("maxWait", "100")); // Changed
+            ref.add(new StringRefAddr("driverClassName", "org.apache.commons.dbcp2.TesterDriver")); //OK
+            final BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
+            basicDataSourceFactory.getObjectInstance(ref, null, null, null);
+            final List<String> messages = StackMessageLog.getAll();
+            assertEquals(2,messages.size());
+            for (String message : messages) {
+                if (message.contains("maxWait")) {
+                    assertTrue(message.contains("use maxWaitMillis"));
+                } else {
+                    assertTrue(message.contains("foo"));
+                    assertTrue(message.contains("Ignoring unknown property"));
+                }
+            }
+        } finally {
+            StackMessageLog.clear();
+            StackMessageLog.unLock();
+        }
+    }   
 }