Index: tests/src/test/java/org/apache/xmlrpc/test/BaseTest.java
===================================================================
--- tests/src/test/java/org/apache/xmlrpc/test/BaseTest.java	(revision 381729)
+++ tests/src/test/java/org/apache/xmlrpc/test/BaseTest.java	(working copy)
@@ -197,6 +197,18 @@
 			}
 			return result;
 		}
+        /** Returns nested single-element arrays pArg levels deep.
+         * The innermost array contains the integers from 0 to pArg-1.
+         * @param pArg Requested array nesting depth.
+         * @return Nested array of integers with the values 0..pArg-1
+         */
+        public Object[] nestedArrayResult(int pArg) {
+            Object[] result = objectArrayResult(pArg);
+            for (int i = 0; i < pArg; i++) {
+                result = new Object[] {result};
+            }
+            return result;
+        }
 		/** Returns a sum over the entries in the map. Each
 		 * key is multiplied with its value.
 		 * @param pArg The map being iterated.
@@ -240,7 +252,7 @@
 			}
 			return count(pNode);
 		}
-		private int count(Node pNode) {
+        private int count(Node pNode) {
 			if (INT_TAG.equals(pNode.getLocalName())  &&  INT_URI.equals(pNode.getNamespaceURI())) {
 				StringBuffer sb = new StringBuffer();
 				for (Node child = pNode.getFirstChild();  child != null;  child = child.getNextSibling()) {
@@ -689,24 +701,59 @@
 	 * object array.
 	 * @throws Exception The test failed.
 	 */
-	public void testObjectArrayResult() throws Exception {
+	public void testNestedArrayResult() throws Exception {
 		for (int i = 0;  i < providers.length;  i++) {
-			testObjectArrayResult(providers[i]);
+			testNestedArrayResult(providers[i]);
 		}
 	}
 
-	private void testObjectArrayResult(ClientProvider pProvider) throws Exception {
-		final Object[] objects = new Object[]{new Integer(0), new Integer(1),
-											  new Integer(2), new Integer(3)};
-		final String methodName = "Remote.objectArrayResult";
+	private void testNestedArrayResult(ClientProvider pProvider) throws Exception {
+		Object[] objects = new Object[]{new Integer(0), new Integer(1),
+                                        new Integer(2), new Integer(3)};
+        final String methodName = "Remote.nestedArrayResult";
 		final Object[] params = new Object[]{new Integer(4)};
 		final XmlRpcClient client = pProvider.getClient();
 		Object result = client.execute(getConfig(pProvider), methodName, params);
-		assertTrue(Arrays.equals(objects, (Object[]) result));
+        // Arrays.deepEquals would be nice here, but it is 1.5-only
+        for (int i = 0; i < 4; i++) {
+            assertTrue(result instanceof Object[]);
+            Object[] ra = (Object[])result;
+            assertEquals(ra.length, 1);
+            result = ra[0];
+        }
+        assertTrue(Arrays.equals(objects, (Object[]) result));
 		result = client.execute(getExConfig(pProvider), methodName, params);
-		assertTrue(Arrays.equals(objects, (Object[]) result));
+        for (int i = 0; i < 4; i++) {
+            assertTrue(result instanceof Object[]);
+            Object[] ra = (Object[])result;
+            assertEquals(ra.length, 1);
+            result = ra[0];
+        }
+        assertTrue(Arrays.equals(objects, (Object[]) result));
 	}
 
+    /** Test, whether we can invoke a method, returning a
+     * nested object array.
+     * @throws Exception The test failed.
+     */
+    public void testObjectArrayResult() throws Exception {
+        for (int i = 0;  i < providers.length;  i++) {
+            testObjectArrayResult(providers[i]);
+        }
+    }
+
+    private void testObjectArrayResult(ClientProvider pProvider) throws Exception {
+        final Object[] objects = new Object[]{new Integer(0), new Integer(1),
+                                              new Integer(2), new Integer(3)};
+        final String methodName = "Remote.objectArrayResult";
+        final Object[] params = new Object[]{new Integer(4)};
+        final XmlRpcClient client = pProvider.getClient();
+        Object result = client.execute(getConfig(pProvider), methodName, params);
+        assertTrue(Arrays.equals(objects, (Object[]) result));
+        result = client.execute(getExConfig(pProvider), methodName, params);
+        assertTrue(Arrays.equals(objects, (Object[]) result));
+    }
+
 	/** Test, whether we can invoke a method, passing a map.
 	 * @throws Exception The test failed.
 	 */
Index: server/src/main/java/org/apache/xmlrpc/server/AbstractReflectiveHandlerMapping.java
===================================================================
--- server/src/main/java/org/apache/xmlrpc/server/AbstractReflectiveHandlerMapping.java	(revision 381729)
+++ server/src/main/java/org/apache/xmlrpc/server/AbstractReflectiveHandlerMapping.java	(working copy)
@@ -1,21 +1,29 @@
 package org.apache.xmlrpc.server;
 
-import org.apache.xmlrpc.XmlRpcRequest;
 import org.apache.xmlrpc.XmlRpcException;
 import org.apache.xmlrpc.XmlRpcHandler;
+import org.apache.xmlrpc.XmlRpcRequest;
 import org.apache.xmlrpc.common.XmlRpcNotAuthorizedException;
+import org.apache.xmlrpc.metadata.XmlRpcListableHandlerMapping;
+import org.apache.xmlrpc.metadata.XmlRpcMetaDataHandler;
+import org.w3c.dom.Node;
 
-import java.util.Map;
-import java.util.HashMap;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 
 /** Abstract base class of handler mappings, which are
  * using reflection.
  */
-public abstract class AbstractReflectiveHandlerMapping implements XmlRpcHandlerMapping {
+public abstract class AbstractReflectiveHandlerMapping implements XmlRpcListableHandlerMapping {
     /** An object implementing this interface may be used
      * to validate user names and passwords.
      */
@@ -28,6 +36,7 @@
     }
 
     protected Map handlerMap = new HashMap();
+    private boolean allowOverloading = false;
     private AuthenticationHandler authenticationHandler;
 
     /** Returns the authentication handler, if any, or null.
@@ -43,6 +52,14 @@
         authenticationHandler = pAuthenticationHandler;
     }
 
+    public boolean getAllowOverloading() {
+        return allowOverloading;
+    }
+
+    protected void setAllowOverloading(boolean allowOverloading) {
+        this.allowOverloading = allowOverloading;
+    }
+
     /** Searches for methods in the given class. For any valid
      * method, it creates an instance of {@link XmlRpcHandler}.
      * Valid methods are defined as follows:
@@ -66,10 +83,10 @@
      * at the same time.
      */
     protected void registerPublicMethods(Map pMap, String pKey,
-    		Class pType, Object pInstance) {
-    	if (pInstance == null) {
-    		throw new NullPointerException("The object instance must not be null.");
-    	}
+                                         Class pType, Object pInstance) {
+        if (pInstance == null) {
+            throw new NullPointerException("The object instance must not be null.");
+        }
         Method[] methods = pType.getMethods();
         for (int i = 0;  i < methods.length;  i++) {
             final Method method = methods[i];
@@ -86,59 +103,22 @@
                 continue;  // Ignore methods from Object.class
             }
             String name = pKey + "." + method.getName();
-            if (!pMap.containsKey(name)) {
-                pMap.put(name, newXmlRpcHandler(pType, pInstance, method));
+            if (pMap.containsKey(name)) {
+                Object o = pMap.get(name);
+                if (o instanceof ReflectiveXmlRpcHandler) {
+                    ReflectiveXmlRpcHandler rh = (ReflectiveXmlRpcHandler) o;
+                    if (rh.getInstance() == pInstance)
+                        rh.addMethod(method);
+                } // else ignore, since we aren't using methods to implement
+            } else {
+                pMap.put(name, new ReflectiveXmlRpcHandler(pType, pInstance, method));
             }
         }
     }
 
-    /** Creates a new instance of {@link XmlRpcHandler}.
-     * @param pClass The class, which was inspected for handler
-     * methods. This is used for error messages only. Typically,
-     * it is the same than <pre>pInstance.getClass()</pre>.
-     * @param pInstance The object, which is being invoked by
-     * the created handler. Typically an instance of
-     * <code>pClass</code>.
-     * @param pMethod The method being invoked.
-     */
-    protected XmlRpcHandler newXmlRpcHandler(final Class pClass,
-    		final Object pInstance, final Method pMethod) {
-    	if (pInstance == null) {
-    		throw new NullPointerException("The object instance must not be null.");
-    	}
-    	return new XmlRpcHandler(){
-            public Object execute(XmlRpcRequest pRequest) throws XmlRpcException {
-                AuthenticationHandler authHandler = getAuthenticationHandler();
-                if (authHandler != null  &&  !authHandler.isAuthorized(pRequest)) {
-                    throw new XmlRpcNotAuthorizedException("Not authorized");
-                }
-                Object[] args = new Object[pRequest.getParameterCount()];
-                for (int j = 0;  j < args.length;  j++) {
-                    args[j] = pRequest.getParameter(j);
-                }
-                try {
-                    return pMethod.invoke(pInstance, args);
-                } catch (IllegalAccessException e) {
-                    throw new XmlRpcException("Illegal access to method "
-                                              + pMethod.getName() + " in class "
-                                              + pClass.getName(), e);
-                } catch (IllegalArgumentException e) {
-                    throw new XmlRpcException("Illegal argument for method "
-                                              + pMethod.getName() + " in class "
-                                              + pClass.getName(), e);
-                } catch (InvocationTargetException e) {
-                    Throwable t = e.getTargetException();
-                    throw new XmlRpcException("Failed to invoke method "
-                                              + pMethod.getName() + " in class "
-                                              + pClass.getName() + ": "
-                                              + t.getMessage(), t);
-                }
-            }
-        };
-    }
-
     /** Returns the {@link XmlRpcHandler} with the given name.
      * @param pHandlerName The handlers name
+     * @return the handler to use
      * @throws XmlRpcNoSuchHandlerException A handler with the given
      * name is unknown.
      */
@@ -150,4 +130,209 @@
         }
         return result;
     }
+
+
+    public String[] getListMethods() throws XmlRpcException {
+        return (String[])handlerMap.keySet().toArray(new String[0]);
+    }
+
+    public String[][] getMethodSignature(String pHandlerName) throws XmlRpcException {
+        Object h = handlerMap.get(pHandlerName);
+        if (h == null)
+            throw new XmlRpcNoSuchHandlerException("No such method: " + pHandlerName);
+        if (h instanceof XmlRpcMetaDataHandler)
+            return ((XmlRpcMetaDataHandler)h).getSignatures();
+        else
+            return new String[0][];
+    }
+
+    public String getMethodHelp(String pHandlerName) throws XmlRpcException {
+        Object h = handlerMap.get(pHandlerName);
+        if (h == null)
+            throw new XmlRpcNoSuchHandlerException("No such method: " + pHandlerName);
+        if (h instanceof XmlRpcMetaDataHandler)
+            return ((XmlRpcMetaDataHandler)h).getMethodHelp();
+        else
+            return "";
+    }
+
+    /** {@link XmlRpcMetaDataHandler} that uses reflection to invoke a public method.
+      */
+    protected class ReflectiveXmlRpcHandler implements XmlRpcMetaDataHandler {
+        private final List methods;
+        private final Object instance;
+        private final Class type;
+
+        /**
+         * @param pClass The class, which was inspected for handler
+         * methods. This is used for error messages only. Typically,
+         * it is the same as <pre>pInstance.getClass()</pre>.
+         * @param pInstance The object, which is being invoked by
+         * the created handler. Typically an pInstance of
+         * <code>pClass</code>.
+         * @param pMethod The pMethod being invoked.
+         */
+        public ReflectiveXmlRpcHandler(Class pClass, Object pInstance, Method pMethod) {
+            this.methods = new ArrayList(5);
+            this.methods.add(pMethod);
+            this.instance = pInstance;
+            this.type = pClass;
+        }
+
+        public Object execute(XmlRpcRequest pRequest) throws XmlRpcException {
+            AuthenticationHandler authHandler = getAuthenticationHandler();
+            if (authHandler != null  &&  !authHandler.isAuthorized(pRequest)) {
+                throw new XmlRpcNotAuthorizedException("Not authorized");
+            }
+
+            Object[] args = new Object[pRequest.getParameterCount()];
+            for (int j = 0;  j < args.length;  j++) {
+                args[j] = pRequest.getParameter(j);
+            }
+
+            Method method;
+            if (allowOverloading)
+                method = findCompatibleMethod(args);
+            else
+                method = (Method)methods.get(0);
+            if (method == null)
+                throw new XmlRpcNoSuchHandlerException("Wrong parameters");
+
+            try {
+                return method.invoke(instance, args);
+            } catch (IllegalAccessException e) {
+                throw new XmlRpcException("Illegal access to method "
+                                          + method.getName() + " in class "
+                                          + type.getName(), e);
+            } catch (IllegalArgumentException e) {
+                throw new XmlRpcException("Illegal argument for method "
+                                          + method.getName() + " in class "
+                                          + type.getName(), e);
+            } catch (InvocationTargetException e) {
+                Throwable t = e.getTargetException();
+                throw new XmlRpcException("Failed to invoke method "
+                                          + method.getName() + " in class "
+                                          + type.getName() + ": "
+                                          + t.getMessage(), t);
+            }
+        }
+
+        private Method findCompatibleMethod(Object[] args) throws XmlRpcException {
+            Method method = null;
+            for (int i = 0; i < methods.size(); i++) {
+                method = (Method)methods.get(i);
+                String[] sig = getMethodSignature(method);
+                Class[] parameterTypes = method.getParameterTypes();
+                if (sig.length - 1 != args.length)
+                    continue;
+                for (int j = 1; j < sig.length; j++) {
+                    if (args[j-1] == null && !parameterTypes[j-1].isPrimitive())
+                        continue; // allow nulls for anything that we can
+
+                    String reqType = mapType(args[j-1].getClass());
+                    if (sig[j].equals(reqType)) {
+                        if (List.class == parameterTypes[j-1])
+                            args[j - 1] = Arrays.asList((Object[]) args[j - 1]);
+                    } else {
+                        // method not compatible, skip to next
+                        method = null;
+                        break;
+                    }
+                }
+                if (method != null) // found a compatible method
+                    break;
+            }
+            return method;
+        }
+
+        public Object getInstance() {
+            return instance;
+        }
+
+        /** Adds an overloaded method to this handler.  It will be invoked on
+         * the handler's instance when no previously added method is found to
+         * be compatible with an incoming signature.
+         * @param method The new method to add.
+         */
+        public void addMethod(Method method) {
+            if (!allowOverloading)
+                return;
+            try {
+                getMethodSignature(method);
+            } catch (XmlRpcException e) {
+                return; // ignore non-exportable methods
+            }
+            methods.add(method);
+        }
+
+        public String[][] getSignatures() throws XmlRpcException {
+            String[][] returnVal = new String[methods.size()][];
+            for (int i = 0; i < methods.size(); i++) {
+                Method m = (Method)methods.get(i);
+                returnVal[i] = getMethodSignature(m);
+            }
+            return returnVal;
+        }
+
+        private String[] getMethodSignature(Method pMethod) throws XmlRpcException {
+            Class returnType = pMethod.getReturnType();
+            Class[] paramTypes = pMethod.getParameterTypes();
+            String[] entry = new String[paramTypes.length+1];
+            entry[0] = mapType(returnType);
+            for (int i = 0; i < paramTypes.length; i++) {
+                entry[i+1] = mapType(paramTypes[i]);
+            }
+            return entry;
+        }
+
+        public String getMethodHelp() throws XmlRpcException {
+            return "";
+        }
+
+        private String mapType(Class type) throws XmlRpcException {
+            if (type == null)
+                return "nil";
+
+            // base types
+            if (type == Integer.TYPE || type == Integer.class)
+                return "int";
+            if (type == Double.TYPE || type == Double.class)
+                return "double";
+            if (type == Boolean.TYPE || type == Boolean.class)
+                return "boolean";
+            if (type == String.class)
+                return "string";
+            if (List.class.isAssignableFrom(type))
+                return "array";
+            if (Map.class.isAssignableFrom(type))
+                return "struct";
+            if (Date.class.isAssignableFrom(type))
+                return "dateTime.iso8601";
+            if (Object[].class.isAssignableFrom(type))
+                return "array";
+            if (type == byte[].class)
+                return "base64";
+            if (type == Object.class)
+                return "undefined";
+
+            // extension types
+            if (type == void.class)
+                return "nil";
+            if (type == Byte.TYPE || type == Byte.class)
+                return "i1";
+            if (type == Short.TYPE || type == Short.class)
+                return "i2";
+            if (type == Long.TYPE || type == Long.class)
+                return "i8";
+            if (type == Float.TYPE || type == Float.class)
+                return "float";
+            if (Node.class.isAssignableFrom(type))
+                return "dom";
+            if (Serializable.class.isAssignableFrom(type)) // last resort
+                return "serializable";
+
+            // give up
+            throw new XmlRpcException("cannot find XML-RPC type for " + type.getName());
+        }
+    }
 }
Index: server/src/main/java/org/apache/xmlrpc/server/XmlRpcSystemImpl.java
===================================================================
--- server/src/main/java/org/apache/xmlrpc/server/XmlRpcSystemImpl.java	(revision 0)
+++ server/src/main/java/org/apache/xmlrpc/server/XmlRpcSystemImpl.java	(revision 0)
@@ -0,0 +1,20 @@
+package org.apache.xmlrpc.server;
+
+import org.apache.xmlrpc.XmlRpcException;
+import org.apache.xmlrpc.metadata.XmlRpcListableHandlerMapping;
+
+public class XmlRpcSystemImpl {
+    private XmlRpcListableHandlerMapping mapping;
+    XmlRpcSystemImpl(XmlRpcListableHandlerMapping mapping) {
+        this.mapping = mapping;
+    }
+    public String[][] methodSignature(String methodName) throws XmlRpcException {
+        return mapping.getMethodSignature(methodName);
+    }
+    public String methodHelp(String methodName) throws XmlRpcException {
+        return mapping.getMethodHelp(methodName);
+    }
+    public String[] listMethods() throws XmlRpcException {
+        return mapping.getListMethods();
+    }
+}
Index: server/src/main/java/org/apache/xmlrpc/server/DynamicHandlerMapping.java
===================================================================
--- server/src/main/java/org/apache/xmlrpc/server/DynamicHandlerMapping.java	(revision 381729)
+++ server/src/main/java/org/apache/xmlrpc/server/DynamicHandlerMapping.java	(working copy)
@@ -1,5 +1,8 @@
 package org.apache.xmlrpc.server;
 
+import org.apache.xmlrpc.XmlRpcException;
+import org.apache.xmlrpc.metadata.XmlRpcListableHandlerMapping;
+
 import java.util.Iterator;
 
 
@@ -18,6 +21,22 @@
         registerPublicMethods(handlerMap, pKey, pHandler.getClass(), pHandler);
     }
 
+    /** Adds all the handlers from the given mapping to this mapping.
+     * An additional class key is added to each handler name.
+     *
+     * @param pKey The added class key
+     * @param pSubMapping The mapping to retrieve handlers from
+     * @throws org.apache.xmlrpc.XmlRpcException if a {@link #getHandler(String)}
+     *                                  or {@link #getListMethods()} call fails.
+     */
+    public void addSubMap(String pKey, XmlRpcListableHandlerMapping pSubMapping) throws XmlRpcException {
+        String[] subHandlerMethods = pSubMapping.getListMethods();
+        for (int i = 0; i < subHandlerMethods.length; i++) {
+            String method = subHandlerMethods[i];
+            handlerMap.put(pKey + "." + method, pSubMapping.getHandler(method));
+        }
+    }
+
     /** Removes all handlers with the given class key.
      */
     public void removeHandler(String pKey) {
Index: server/src/main/java/org/apache/xmlrpc/server/PropertyHandlerMapping.java
===================================================================
--- server/src/main/java/org/apache/xmlrpc/server/PropertyHandlerMapping.java	(revision 381729)
+++ server/src/main/java/org/apache/xmlrpc/server/PropertyHandlerMapping.java	(working copy)
@@ -21,6 +21,8 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Properties;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 
 import org.apache.xmlrpc.XmlRpcException;
 
@@ -45,11 +47,13 @@
      * @param pClassLoader Classloader being used to load the classes.
      * @param pURL The URL, from which the property file is being
      * loaded.
+     * @param pAllowOverloading Whether to support function overloading.
      * @throws IOException Loading the property file failed.
      * @throws XmlRpcException Initializing the handlers failed.
      */
-    public PropertyHandlerMapping(ClassLoader pClassLoader, URL pURL)
+    public PropertyHandlerMapping(ClassLoader pClassLoader, URL pURL, boolean pAllowOverloading)
             throws IOException, XmlRpcException {
+        setAllowOverloading(pAllowOverloading);
         handlerMap = load(pClassLoader, pURL);
     }
 
@@ -58,11 +62,13 @@
      * @param pClassLoader Classloader being used to locate
      * the resource.
      * @param pResource Resource being loaded.
+     * @param pAllowOverloading Whether to support function overloading.
      * @throws IOException Loading the property file failed.
      * @throws XmlRpcException Initializing the handlers failed.
      */
-    public PropertyHandlerMapping(ClassLoader pClassLoader, String pResource)
+    public PropertyHandlerMapping(ClassLoader pClassLoader, String pResource, boolean pAllowOverloading)
             throws IOException, XmlRpcException {
+        setAllowOverloading(pAllowOverloading);
         URL url = pClassLoader.getResource(pResource);
         if (url == null) {
             throw new IOException("Unable to locate resource " + pResource);
@@ -87,13 +93,31 @@
             if (c == null) {
                 throw new XmlRpcException(0, "Loading class " + value + " returned null.");
             }
+            Constructor[] constructors = c.getConstructors();
+            Constructor constructor = null;
+            Object[] constructArgs = null;
+            for (int i = 0; i < constructors.length; i++) {
+                Class[] parmTypes = constructors[i].getParameterTypes();
+                if (parmTypes.length == 0) {
+                    constructor = constructors[i];
+                    constructArgs = new Object[0];
+                } else if (parmTypes.length == 1 &&
+                           parmTypes[0].isAssignableFrom(getClass())) {
+                    constructArgs = new Object[] {this};
+                    break; // prefer this constructor when possible
+                }
+            }
+            if (constructor == null)
+                throw new XmlRpcException("No usable constructor for class " + c.getName());
             final Object o;
             try {
-                o = c.newInstance();
+                o = constructor.newInstance(constructArgs);
             } catch (InstantiationException e) {
                 throw new XmlRpcException("Failed to instantiate class " + c.getName(), e);
             } catch (IllegalAccessException e) {
                 throw new XmlRpcException("Illegal access when instantiating class " + c.getName(), e);
+            } catch (InvocationTargetException e) {
+                throw new XmlRpcException("Constructor threw exception when instantiating class " + c.getName(), e);
             }
             registerPublicMethods(map, key, c, o);
         }
