You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by th...@apache.org on 2017/07/07 02:06:15 UTC

[40/50] commons-dbutils git commit: DBUTILS-124 Find column, property handlers using java's spi

DBUTILS-124 Find column, property handlers using java's spi

Move out the hard coded values of column and property types that dbutils
can handle into spi files.  This should also allow users of this library
to add their own handlers later.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/dbutils/trunk@1674745 13f79535-47bb-0310-9956-ffa450edef68


Project: http://git-wip-us.apache.org/repos/asf/commons-dbutils/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-dbutils/commit/6f6e4bc0
Tree: http://git-wip-us.apache.org/repos/asf/commons-dbutils/tree/6f6e4bc0
Diff: http://git-wip-us.apache.org/repos/asf/commons-dbutils/diff/6f6e4bc0

Branch: refs/heads/master
Commit: 6f6e4bc01a267342ba43e7c1f2625250d8539293
Parents: aa276d1
Author: Carl Franklin Hall <th...@apache.org>
Authored: Mon Apr 20 06:32:36 2015 +0000
Committer: Carl Franklin Hall <th...@apache.org>
Committed: Mon Apr 20 06:32:36 2015 +0000

----------------------------------------------------------------------
 .../apache/commons/dbutils/BeanProcessor.java   | 145 ++++++++-----------
 .../apache/commons/dbutils/ColumnHandler.java   |  46 ++++++
 .../apache/commons/dbutils/PropertyHandler.java |  44 ++++++
 .../handlers/columns/BooleanColumnHandler.java  |  34 +++++
 .../handlers/columns/ByteColumnHandler.java     |  34 +++++
 .../handlers/columns/DoubleColumnHandler.java   |  34 +++++
 .../handlers/columns/FloatColumnHandler.java    |  34 +++++
 .../handlers/columns/IntegerColumnHandler.java  |  34 +++++
 .../handlers/columns/LongColumnHandler.java     |  34 +++++
 .../handlers/columns/SQLXMLColumnHandler.java   |  35 +++++
 .../handlers/columns/ShortColumnHandler.java    |  34 +++++
 .../handlers/columns/StringColumnHandler.java   |  34 +++++
 .../columns/TimestampColumnHandler.java         |  35 +++++
 .../properties/DatePropertyHandler.java         |  60 ++++++++
 .../properties/StringEnumPropertyHandler.java   |  31 ++++
 .../org.apache.commons.dbutils.ColumnHandler    |  26 ++++
 .../org.apache.commons.dbutils.PropertyHandler  |  18 +++
 .../commons/dbutils/ServiceLoaderTest.java      |  92 ++++++++++++
 .../handlers/columns/TestColumnHandler.java     |  35 +++++
 .../properties/PropertyHandlerTest.java         |  62 ++++++++
 .../properties/TestPropertyHandler.java         |  32 ++++
 .../org.apache.commons.dbutils.ColumnHandler    |  17 +++
 .../org.apache.commons.dbutils.PropertyHandler  |  17 +++
 23 files changed, 883 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/BeanProcessor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/BeanProcessor.java b/src/main/java/org/apache/commons/dbutils/BeanProcessor.java
index 483719c..7580655 100644
--- a/src/main/java/org/apache/commons/dbutils/BeanProcessor.java
+++ b/src/main/java/org/apache/commons/dbutils/BeanProcessor.java
@@ -20,18 +20,18 @@ import java.beans.BeanInfo;
 import java.beans.IntrospectionException;
 import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
-import java.sql.SQLXML;
-import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.ServiceLoader;
 
 /**
  * <p>
@@ -66,6 +66,20 @@ public class BeanProcessor {
     private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();
 
     /**
+     * ServiceLoader to find <code>ColumnHandler</code> implementations on the classpath.  The iterator for this is
+     * lazy and each time <code>iterator()</code> is called.
+     */
+    // FIXME: I think this instantiates new handlers on each iterator() call. This might be worth caching upfront.
+    private static final ServiceLoader<ColumnHandler> columnHandlers = ServiceLoader.load(ColumnHandler.class);
+
+    /**
+     * ServiceLoader to find <code>PropertyHandler</code> implementations on the classpath.  The iterator for this is
+     * lazy and each time <code>iterator()</code> is called.
+     */
+    // FIXME: I think this instantiates new handlers on each iterator() call. This might be worth caching upfront.
+    private static final ServiceLoader<PropertyHandler> propertyHandlers = ServiceLoader.load(PropertyHandler.class);
+
+    /**
      * ResultSet column to bean property name overrides.
      */
     private final Map<String, String> columnToPropertyOverrides;
@@ -277,39 +291,26 @@ public class BeanProcessor {
 
         Method setter = getWriteMethod(target, prop, value);
 
-        if (setter == null) {
+        if (setter == null || setter.getParameterTypes().length != 1) {
             return;
         }
 
-        Class<?>[] params = setter.getParameterTypes();
         try {
-            // convert types for some popular ones
-            if (value instanceof java.util.Date) {
-                final String targetType = params[0].getName();
-                if ("java.sql.Date".equals(targetType)) {
-                    value = new java.sql.Date(((java.util.Date) value).getTime());
-                } else
-                if ("java.sql.Time".equals(targetType)) {
-                    value = new java.sql.Time(((java.util.Date) value).getTime());
-                } else
-                if ("java.sql.Timestamp".equals(targetType)) {
-                    Timestamp tsValue = (Timestamp) value;
-                    int nanos = tsValue.getNanos();
-                    value = new java.sql.Timestamp(tsValue.getTime());
-                    ((Timestamp) value).setNanos(nanos);
+            Class<?> firstParam = setter.getParameterTypes()[0];
+            for (PropertyHandler handler : propertyHandlers) {
+                if (handler.match(firstParam, value)) {
+                    value = handler.apply(firstParam, value);
+                    break;
                 }
-            } else
-            if (value instanceof String && params[0].isEnum()) {
-                value = Enum.valueOf(params[0].asSubclass(Enum.class), (String) value);
             }
 
             // Don't call setter if the value object isn't the right type
-            if (this.isCompatibleType(value, params[0])) {
+            if (this.isCompatibleType(value, firstParam)) {
                 setter.invoke(target, new Object[]{value});
             } else {
               throw new SQLException(
                   "Cannot set " + prop.getName() + ": incompatible types, cannot convert "
-                  + value.getClass().getName() + " to " + params[0].getName());
+                  + value.getClass().getName() + " to " + firstParam.getName());
                   // value cannot be null here because isCompatibleType allows null
             }
 
@@ -340,36 +341,40 @@ public class BeanProcessor {
      */
     private boolean isCompatibleType(Object value, Class<?> type) {
         // Do object check first, then primitives
-        if (value == null || type.isInstance(value)) {
-            return true;
-
-        } else if (type.equals(Integer.TYPE) && value instanceof Integer) {
-            return true;
-
-        } else if (type.equals(Long.TYPE) && value instanceof Long) {
+        if (value == null
+                || type.isInstance(value)
+                || (type.isPrimitive() && matchesPrimitive(type, value.getClass()))) {
             return true;
 
-        } else if (type.equals(Double.TYPE) && value instanceof Double) {
-            return true;
-
-        } else if (type.equals(Float.TYPE) && value instanceof Float) {
-            return true;
-
-        } else if (type.equals(Short.TYPE) && value instanceof Short) {
-            return true;
-
-        } else if (type.equals(Byte.TYPE) && value instanceof Byte) {
-            return true;
+        }
+        return false;
 
-        } else if (type.equals(Character.TYPE) && value instanceof Character) {
-            return true;
+    }
 
-        } else if (type.equals(Boolean.TYPE) && value instanceof Boolean) {
-            return true;
+    /**
+     * Check whether a value is of the same primitive type as <code>targetType</code>.
+     *
+     * @param targetType The primitive type to target.
+     * @param valueType The value to match to the primitive type.
+     * @return Whether <code>valueType</code> can be coerced (e.g. autoboxed) into <code>targetType</code>.
+     */
+    private boolean matchesPrimitive(Class<?> targetType, Class<?> valueType) {
+        try {
+            // see if there is a "TYPE" field.  This is present for primitive wrappers.
+            Field typeField = valueType.getField("TYPE");
+            Object primitiveValueType = typeField.get(valueType);
 
+            if (targetType == primitiveValueType) {
+                return true;
+            }
+        } catch (NoSuchFieldException e) {
+            // lacking the TYPE field is a good sign that we're not working with a primitive wrapper.
+            // we can't match for compatibility
+        } catch (IllegalAccessException e) {
+            // an inaccessible TYPE field is a good sign that we're not working with a primitive wrapper.
+            // nothing to do.  we can't match for compatibility
         }
         return false;
-
     }
 
     /**
@@ -507,49 +512,21 @@ public class BeanProcessor {
     protected Object processColumn(ResultSet rs, int index, Class<?> propType)
         throws SQLException {
 
-        if ( !propType.isPrimitive() && rs.getObject(index) == null ) {
+        Object retval = rs.getObject(index);
+
+        if ( !propType.isPrimitive() && retval == null ) {
             return null;
         }
 
-        if (propType.equals(String.class)) {
-            return rs.getString(index);
-
-        } else if (
-            propType.equals(Integer.TYPE) || propType.equals(Integer.class)) {
-            return Integer.valueOf(rs.getInt(index));
-
-        } else if (
-            propType.equals(Boolean.TYPE) || propType.equals(Boolean.class)) {
-            return Boolean.valueOf(rs.getBoolean(index));
-
-        } else if (propType.equals(Long.TYPE) || propType.equals(Long.class)) {
-            return Long.valueOf(rs.getLong(index));
-
-        } else if (
-            propType.equals(Double.TYPE) || propType.equals(Double.class)) {
-            return Double.valueOf(rs.getDouble(index));
-
-        } else if (
-            propType.equals(Float.TYPE) || propType.equals(Float.class)) {
-            return Float.valueOf(rs.getFloat(index));
-
-        } else if (
-            propType.equals(Short.TYPE) || propType.equals(Short.class)) {
-            return Short.valueOf(rs.getShort(index));
-
-        } else if (propType.equals(Byte.TYPE) || propType.equals(Byte.class)) {
-            return Byte.valueOf(rs.getByte(index));
-
-        } else if (propType.equals(Timestamp.class)) {
-            return rs.getTimestamp(index);
-
-        } else if (propType.equals(SQLXML.class)) {
-            return rs.getSQLXML(index);
-
-        } else {
-            return rs.getObject(index);
+        for (ColumnHandler handler : columnHandlers) {
+            if (handler.match(propType)) {
+                retval = handler.apply(rs, index);
+                break;
+            }
         }
 
+        return retval;
+
     }
 
 }

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/ColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/ColumnHandler.java b/src/main/java/org/apache/commons/dbutils/ColumnHandler.java
new file mode 100644
index 0000000..c520e60
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/ColumnHandler.java
@@ -0,0 +1,46 @@
+/*
+ * 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.commons.dbutils;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * Interface to define how implementations can interact with column handling when constructing a bean from a
+ * {@link java.sql.ResultSet}.  ColumnHandlers do the work of retrieving data correctly from the <code>ResultSet</code>.
+ */
+public interface ColumnHandler {
+    /**
+     * Test whether this <code>ColumnHandler</code> wants to handle a column targetted for a value type matching
+     * <code>propType</code>.
+     *
+     * @param propType The type of the target parameter.
+     * @return true is this property handler can/wants to handle this value; false otherwise.
+     */
+    boolean match(Class<?> propType);
+
+    /**
+     * Do the work required to retrieve and store a column from <code>ResultSet</code> into something of type
+     * <code>propType</code>. This method is called only if this handler responded <code>true</code> after a
+     * call to {@link #match(Class)}.
+     *
+     * @param rs The result set to get data from.  This should be moved to the correct row already.
+     * @param columnIndex The position of the column to retrieve.
+     * @return The converted value or the original value if something doesn't work out.
+     */
+    Object apply(ResultSet rs, int columnIndex) throws SQLException;
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/PropertyHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/PropertyHandler.java b/src/main/java/org/apache/commons/dbutils/PropertyHandler.java
new file mode 100644
index 0000000..952179c
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/PropertyHandler.java
@@ -0,0 +1,44 @@
+/*
+ * 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.commons.dbutils;
+
+/**
+ * Interface to define how implementations can interact with property handling when constructing a bean from a
+ * {@link java.sql.ResultSet}.  PropertyHandlers do the work of coercing a value into the target type required.
+ */
+public interface PropertyHandler {
+
+    /**
+     * Test whether this <code>PropertyHandler</code> wants to handle setting <code>value</code> into something of type
+     * <code>parameter</code>.
+     *
+     * @param parameter The type of the target parameter.
+     * @param value The value to be set.
+     * @return true is this property handler can/wants to handle this value; false otherwise.
+     */
+    boolean match(Class<?> parameter, Object value);
+
+    /**
+     * Do the work required to store <code>value</code> into something of type <code>parameter</code>. This method is
+     * called only if this handler responded <code>true</code> after a call to {@link #match(Class, Object)}.
+     *
+     * @param parameter The type of the target parameter.
+     * @param value The value to be set.
+     * @return The converted value or the original value if something doesn't work out.
+     */
+    Object apply(Class<?> parameter, Object value);
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/BooleanColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/BooleanColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/BooleanColumnHandler.java
new file mode 100644
index 0000000..a7d1189
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/BooleanColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class BooleanColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Boolean.TYPE) || propType.equals(Boolean.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return Boolean.valueOf(rs.getBoolean(columnIndex));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/ByteColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/ByteColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/ByteColumnHandler.java
new file mode 100644
index 0000000..4731452
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/ByteColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class ByteColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Byte.TYPE) || propType.equals(Byte.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return Byte.valueOf(rs.getByte(columnIndex));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/DoubleColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/DoubleColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/DoubleColumnHandler.java
new file mode 100644
index 0000000..dbfb58b
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/DoubleColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class DoubleColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Double.TYPE) || propType.equals(Double.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return Double.valueOf(rs.getDouble(columnIndex));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/FloatColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/FloatColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/FloatColumnHandler.java
new file mode 100644
index 0000000..8f7edf1
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/FloatColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class FloatColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Float.TYPE) || propType.equals(Float.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return Float.valueOf(rs.getFloat(columnIndex));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/IntegerColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/IntegerColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/IntegerColumnHandler.java
new file mode 100644
index 0000000..7d7d00e
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/IntegerColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class IntegerColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Integer.TYPE) || propType.equals(Integer.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return Integer.valueOf(rs.getInt(columnIndex));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/LongColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/LongColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/LongColumnHandler.java
new file mode 100644
index 0000000..75dec28
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/LongColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class LongColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Long.TYPE) || propType.equals(Long.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return Long.valueOf(rs.getLong(columnIndex));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/SQLXMLColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/SQLXMLColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/SQLXMLColumnHandler.java
new file mode 100644
index 0000000..19166fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/SQLXMLColumnHandler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class SQLXMLColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(SQLXML.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return rs.getSQLXML(columnIndex);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/ShortColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/ShortColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/ShortColumnHandler.java
new file mode 100644
index 0000000..7ce36e4
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/ShortColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class ShortColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Short.TYPE) || propType.equals(Short.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return Short.valueOf(rs.getShort(columnIndex));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/StringColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/StringColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/StringColumnHandler.java
new file mode 100644
index 0000000..6f07c78
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/StringColumnHandler.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class StringColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(String.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return rs.getString(columnIndex);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/columns/TimestampColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/columns/TimestampColumnHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/columns/TimestampColumnHandler.java
new file mode 100644
index 0000000..4e2195b
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/columns/TimestampColumnHandler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class TimestampColumnHandler implements ColumnHandler {
+    @Override
+    public boolean match(Class<?> propType) {
+        return propType.equals(Timestamp.class);
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return rs.getTimestamp(columnIndex);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/properties/DatePropertyHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/properties/DatePropertyHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/properties/DatePropertyHandler.java
new file mode 100644
index 0000000..9e623dd
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/properties/DatePropertyHandler.java
@@ -0,0 +1,60 @@
+/*
+ * 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.commons.dbutils.handlers.properties;
+
+import java.sql.Timestamp;
+
+import org.apache.commons.dbutils.PropertyHandler;
+
+public class DatePropertyHandler implements PropertyHandler {
+    @Override
+    public boolean match(Class<?> parameter, Object value) {
+        if (value instanceof java.util.Date) {
+            final String targetType = parameter.getName();
+            if ("java.sql.Date".equals(targetType)) {
+                return true;
+            } else
+            if ("java.sql.Time".equals(targetType)) {
+                return true;
+            } else
+            if ("java.sql.Timestamp".equals(targetType)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public Object apply(Class<?> parameter, Object value) {
+        final String targetType = parameter.getName();
+        if ("java.sql.Date".equals(targetType)) {
+            value = new java.sql.Date(((java.util.Date) value).getTime());
+        } else
+        if ("java.sql.Time".equals(targetType)) {
+            value = new java.sql.Time(((java.util.Date) value).getTime());
+        } else
+        if ("java.sql.Timestamp".equals(targetType)) {
+            Timestamp tsValue = (Timestamp) value;
+            int nanos = tsValue.getNanos();
+            value = new java.sql.Timestamp(tsValue.getTime());
+            ((Timestamp) value).setNanos(nanos);
+        }
+
+        return value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/java/org/apache/commons/dbutils/handlers/properties/StringEnumPropertyHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/properties/StringEnumPropertyHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/properties/StringEnumPropertyHandler.java
new file mode 100644
index 0000000..67e309f
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils/handlers/properties/StringEnumPropertyHandler.java
@@ -0,0 +1,31 @@
+/*
+ * 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.commons.dbutils.handlers.properties;
+
+import org.apache.commons.dbutils.PropertyHandler;
+
+public class StringEnumPropertyHandler implements PropertyHandler {
+    @Override
+    public boolean match(Class<?> parameter, Object value) {
+        return value instanceof String && parameter.isEnum();
+    }
+
+    @Override
+    public Object apply(Class<?> parameter, Object value) {
+        return Enum.valueOf(parameter.asSubclass(Enum.class), (String) value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler
----------------------------------------------------------------------
diff --git a/src/main/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler b/src/main/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler
new file mode 100644
index 0000000..7485e1d
--- /dev/null
+++ b/src/main/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+org.apache.commons.dbutils.handlers.columns.StringColumnHandler
+org.apache.commons.dbutils.handlers.columns.IntegerColumnHandler
+org.apache.commons.dbutils.handlers.columns.BooleanColumnHandler
+org.apache.commons.dbutils.handlers.columns.LongColumnHandler
+org.apache.commons.dbutils.handlers.columns.DoubleColumnHandler
+org.apache.commons.dbutils.handlers.columns.FloatColumnHandler
+org.apache.commons.dbutils.handlers.columns.ShortColumnHandler
+org.apache.commons.dbutils.handlers.columns.ByteColumnHandler
+org.apache.commons.dbutils.handlers.columns.TimestampColumnHandler
+org.apache.commons.dbutils.handlers.columns.SQLXMLColumnHandler

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/main/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler
----------------------------------------------------------------------
diff --git a/src/main/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler b/src/main/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler
new file mode 100644
index 0000000..8eb3bb5
--- /dev/null
+++ b/src/main/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+org.apache.commons.dbutils.handlers.properties.DatePropertyHandler
+org.apache.commons.dbutils.handlers.properties.StringEnumPropertyHandler
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/test/java/org/apache/commons/dbutils/ServiceLoaderTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/dbutils/ServiceLoaderTest.java b/src/test/java/org/apache/commons/dbutils/ServiceLoaderTest.java
new file mode 100644
index 0000000..52f8204
--- /dev/null
+++ b/src/test/java/org/apache/commons/dbutils/ServiceLoaderTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.commons.dbutils;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ServiceLoader;
+
+import org.apache.commons.dbutils.handlers.columns.TestColumnHandler;
+import org.apache.commons.dbutils.handlers.properties.TestPropertyHandler;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ServiceLoaderTest {
+    private ServiceLoader<ColumnHandler> columns;
+    private ServiceLoader<PropertyHandler> properties;
+
+    @Before
+    public void setUp() {
+        columns = ServiceLoader.load(ColumnHandler.class);
+        properties = ServiceLoader.load(PropertyHandler.class);
+    }
+
+    @Test
+    public void testFindsLocalColumnHandler() {
+        boolean found = false;
+        for (ColumnHandler handler : columns) {
+            // this class is defined outside of the main classes in dbutils
+            if (handler instanceof TestColumnHandler) {
+                found = true;
+            }
+        }
+
+        assertTrue(found);
+    }
+
+    @Test
+    public void testFindsLocalPropertyHandler() {
+        boolean found = false;
+        for (PropertyHandler handler : properties) {
+            // this class is defined outside of the main classes in dbutils
+            if (handler instanceof TestPropertyHandler) {
+                found = true;
+            }
+        }
+
+        assertTrue(found);
+    }
+
+    /**
+     * Verifying 'more than 1' shows that we found more than we loaded locally which assumes the core handlers
+     * were loaded, too.
+     */
+    @Test
+    public void testFindMoreThanLocalColumns() {
+        int count = 0;
+        for (ColumnHandler handler : columns) {
+            count++;
+        }
+
+        assertTrue(count > 1);
+    }
+
+    /**
+     * Verifying 'more than 1' shows that we found more than we loaded locally which assumes the core handlers
+     * were loaded, too.
+     */
+    @Test
+    public void testFindMoreThanLocalProperties() {
+        int count = 0;
+        for (PropertyHandler handler : properties) {
+            count++;
+        }
+
+        assertTrue(count > 1);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/test/java/org/apache/commons/dbutils/handlers/columns/TestColumnHandler.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/dbutils/handlers/columns/TestColumnHandler.java b/src/test/java/org/apache/commons/dbutils/handlers/columns/TestColumnHandler.java
new file mode 100644
index 0000000..f36d0a0
--- /dev/null
+++ b/src/test/java/org/apache/commons/dbutils/handlers/columns/TestColumnHandler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commons.dbutils.handlers.columns;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ColumnHandler;
+
+public class TestColumnHandler implements ColumnHandler {
+
+    @Override
+    public boolean match(Class<?> propType) {
+        return false;
+    }
+
+    @Override
+    public Object apply(ResultSet rs, int columnIndex) throws SQLException {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/test/java/org/apache/commons/dbutils/handlers/properties/PropertyHandlerTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/dbutils/handlers/properties/PropertyHandlerTest.java b/src/test/java/org/apache/commons/dbutils/handlers/properties/PropertyHandlerTest.java
new file mode 100644
index 0000000..8d605fe
--- /dev/null
+++ b/src/test/java/org/apache/commons/dbutils/handlers/properties/PropertyHandlerTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.commons.dbutils.handlers.properties;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ServiceLoader;
+
+import org.apache.commons.dbutils.PropertyHandler;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PropertyHandlerTest {
+    private ServiceLoader<PropertyHandler> loader;
+
+    @Before
+    public void setUp() {
+        loader = ServiceLoader.load(PropertyHandler.class);
+    }
+
+    @Test
+    public void testServiceLoaderFindsMultipleRegistries() {
+        boolean found = false;
+        for (PropertyHandler handler : loader) {
+            // this class is defined outside of the main classes of dbutils
+            if (handler instanceof TestPropertyHandler) {
+                found = true;
+            }
+        }
+
+        assertTrue(found);
+    }
+
+    /**
+     * Verifying 'more than 1' shows that we found more than we loaded locally which assumes the core handlers
+     * were loaded, too.
+     */
+    @Test
+    public void testFoundMoreThanLocal() {
+        int count = 0;
+        for (PropertyHandler handler : loader) {
+            count++;
+        }
+
+        assertTrue(count > 1);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/test/java/org/apache/commons/dbutils/handlers/properties/TestPropertyHandler.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/dbutils/handlers/properties/TestPropertyHandler.java b/src/test/java/org/apache/commons/dbutils/handlers/properties/TestPropertyHandler.java
new file mode 100644
index 0000000..c6344b3
--- /dev/null
+++ b/src/test/java/org/apache/commons/dbutils/handlers/properties/TestPropertyHandler.java
@@ -0,0 +1,32 @@
+/*
+ * 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.commons.dbutils.handlers.properties;
+
+import org.apache.commons.dbutils.PropertyHandler;
+
+public class TestPropertyHandler implements PropertyHandler {
+
+    @Override
+    public boolean match(Class<?> parameter, Object value) {
+        return false;
+    }
+
+    @Override
+    public Object apply(Class<?> parameter, Object value) {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/test/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler
----------------------------------------------------------------------
diff --git a/src/test/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler b/src/test/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler
new file mode 100644
index 0000000..0206204
--- /dev/null
+++ b/src/test/resources/META-INF/services/org.apache.commons.dbutils.ColumnHandler
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+org.apache.commons.dbutils.handlers.columns.TestColumnHandler

http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/6f6e4bc0/src/test/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler
----------------------------------------------------------------------
diff --git a/src/test/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler b/src/test/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler
new file mode 100644
index 0000000..0d89c03
--- /dev/null
+++ b/src/test/resources/META-INF/services/org.apache.commons.dbutils.PropertyHandler
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+org.apache.commons.dbutils.handlers.properties.TestPropertyHandler