253 lines
9.6 KiB
C#
253 lines
9.6 KiB
C#
namespace Nwc.XmlRpc
|
|
{
|
|
using System;
|
|
using System.Collections;
|
|
using System.Reflection;
|
|
|
|
/// <summary> XML-RPC System object implementation of extended specifications.</summary>
|
|
[XmlRpcExposed]
|
|
public class XmlRpcSystemObject
|
|
{
|
|
private XmlRpcServer _server;
|
|
static private IDictionary _methodHelp = new Hashtable();
|
|
|
|
/// <summary>Static <c>IDictionary</c> to hold mappings of method name to associated documentation String</summary>
|
|
static public IDictionary MethodHelp
|
|
{
|
|
get { return _methodHelp; }
|
|
}
|
|
|
|
/// <summary>Constructor.</summary>
|
|
/// <param name="server"><c>XmlRpcServer</c> server to be the system object for.</param>
|
|
public XmlRpcSystemObject(XmlRpcServer server)
|
|
{
|
|
_server = server;
|
|
server.Add("system", this);
|
|
_methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description.");
|
|
}
|
|
|
|
/// <summary>Invoke a method on a given object.</summary>
|
|
/// <remarks>Using reflection, and respecting the <c>XmlRpcExposed</c> attribute,
|
|
/// invoke the <paramref>methodName</paramref> method on the <paramref>target</paramref>
|
|
/// instance with the <paramref>parameters</paramref> provided. All this packages other <c>Invoke</c> methods
|
|
/// end up calling this.</remarks>
|
|
/// <returns><c>Object</c> the value the invoked method returns.</returns>
|
|
/// <exception cref="XmlRpcException">If method does not exist, is not exposed, parameters invalid, or invocation
|
|
/// results in an exception. Note, the <c>XmlRpcException.Code</c> will indicate cause.</exception>
|
|
static public Object Invoke(Object target, String methodName, IList parameters)
|
|
{
|
|
if (target == null)
|
|
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
|
|
XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Invalid target object.");
|
|
|
|
Type type = target.GetType();
|
|
MethodInfo method = type.GetMethod(methodName);
|
|
|
|
try
|
|
{
|
|
if (!XmlRpcExposedAttribute.ExposedMethod(target, methodName))
|
|
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
|
|
XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Method " + methodName + " is not exposed.");
|
|
}
|
|
catch (MissingMethodException me)
|
|
{
|
|
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
|
|
XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": " + me.Message);
|
|
}
|
|
|
|
Object[] args = new Object[parameters.Count];
|
|
|
|
int index = 0;
|
|
foreach (Object arg in parameters)
|
|
{
|
|
args[index] = arg;
|
|
index++;
|
|
}
|
|
|
|
try
|
|
{
|
|
Object retValue = method.Invoke(target, args);
|
|
if (retValue == null)
|
|
throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR,
|
|
XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": Method returned NULL.");
|
|
return retValue;
|
|
}
|
|
catch (XmlRpcException e)
|
|
{
|
|
throw e;
|
|
}
|
|
catch (ArgumentException ae)
|
|
{
|
|
Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + ae.Message,
|
|
LogLevel.Information);
|
|
String call = methodName + "( ";
|
|
foreach (Object o in args)
|
|
{
|
|
call += o.GetType().Name;
|
|
call += " ";
|
|
}
|
|
call += ")";
|
|
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS,
|
|
XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement type mismatch invoking " + call);
|
|
}
|
|
catch (TargetParameterCountException tpce)
|
|
{
|
|
Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + tpce.Message,
|
|
LogLevel.Information);
|
|
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS,
|
|
XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement count mismatch invoking " + methodName);
|
|
}
|
|
catch (TargetInvocationException tie)
|
|
{
|
|
throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR,
|
|
XmlRpcErrorCodes.APPLICATION_ERROR_MSG + " Invoked method " + methodName + ": " + tie.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>List methods available on all handlers of this server.</summary>
|
|
/// <returns><c>IList</c> An array of <c>Strings</c>, each <c>String</c> will have form "object.method".</returns>
|
|
[XmlRpcExposed]
|
|
public IList listMethods()
|
|
{
|
|
IList methods = new ArrayList();
|
|
Boolean considerExposure;
|
|
|
|
foreach (DictionaryEntry handlerEntry in _server)
|
|
{
|
|
considerExposure = XmlRpcExposedAttribute.IsExposed(handlerEntry.Value.GetType());
|
|
|
|
foreach (MemberInfo mi in handlerEntry.Value.GetType().GetMembers())
|
|
{
|
|
if (mi.MemberType != MemberTypes.Method)
|
|
continue;
|
|
|
|
if (!((MethodInfo)mi).IsPublic)
|
|
continue;
|
|
|
|
if (considerExposure && !XmlRpcExposedAttribute.IsExposed(mi))
|
|
continue;
|
|
|
|
methods.Add(handlerEntry.Key + "." + mi.Name);
|
|
}
|
|
}
|
|
|
|
return methods;
|
|
}
|
|
|
|
/// <summary>Given a method name return the possible signatures for it.</summary>
|
|
/// <param name="name"><c>String</c> The object.method name to look up.</param>
|
|
/// <returns><c>IList</c> Of arrays of signatures.</returns>
|
|
[XmlRpcExposed]
|
|
public IList methodSignature(String name)
|
|
{
|
|
IList signatures = new ArrayList();
|
|
int index = name.IndexOf('.');
|
|
|
|
if (index < 0)
|
|
return signatures;
|
|
|
|
String oName = name.Substring(0, index);
|
|
Object obj = _server[oName];
|
|
|
|
if (obj == null)
|
|
return signatures;
|
|
|
|
MemberInfo[] mi = obj.GetType().GetMember(name.Substring(index + 1));
|
|
|
|
if (mi == null || mi.Length != 1) // for now we want a single signature
|
|
return signatures;
|
|
|
|
MethodInfo method;
|
|
|
|
try
|
|
{
|
|
method = (MethodInfo)mi[0];
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.WriteEntry("Attempted methodSignature call on " + mi[0] + " caused: " + e,
|
|
LogLevel.Information);
|
|
return signatures;
|
|
}
|
|
|
|
if (!method.IsPublic)
|
|
return signatures;
|
|
|
|
IList signature = new ArrayList();
|
|
signature.Add(method.ReturnType.Name);
|
|
|
|
foreach (ParameterInfo param in method.GetParameters())
|
|
{
|
|
signature.Add(param.ParameterType.Name);
|
|
}
|
|
|
|
|
|
signatures.Add(signature);
|
|
|
|
return signatures;
|
|
}
|
|
|
|
/// <summary>Help for given method signature. Not implemented yet.</summary>
|
|
/// <param name="name"><c>String</c> The object.method name to look up.</param>
|
|
/// <returns><c>String</c> help text. Rich HTML text.</returns>
|
|
[XmlRpcExposed]
|
|
public String methodHelp(String name)
|
|
{
|
|
String help = null;
|
|
|
|
try
|
|
{
|
|
help = (String)_methodHelp[_server.MethodName(name)];
|
|
}
|
|
catch (XmlRpcException e)
|
|
{
|
|
throw e;
|
|
}
|
|
catch (Exception) { /* ignored */ };
|
|
|
|
if (help == null)
|
|
help = "No help available for: " + name;
|
|
|
|
return help;
|
|
}
|
|
|
|
/// <summary>Boxcarring support method.</summary>
|
|
/// <param name="calls"><c>IList</c> of calls</param>
|
|
/// <returns><c>ArrayList</c> of results/faults.</returns>
|
|
[XmlRpcExposed]
|
|
public IList multiCall(IList calls)
|
|
{
|
|
IList responses = new ArrayList();
|
|
XmlRpcResponse fault = new XmlRpcResponse();
|
|
|
|
foreach (IDictionary call in calls)
|
|
{
|
|
try
|
|
{
|
|
XmlRpcRequest req = new XmlRpcRequest((String)call[XmlRpcXmlTokens.METHOD_NAME],
|
|
(ArrayList)call[XmlRpcXmlTokens.PARAMS]);
|
|
Object results = _server.Invoke(req);
|
|
IList response = new ArrayList();
|
|
response.Add(results);
|
|
responses.Add(response);
|
|
}
|
|
catch (XmlRpcException e)
|
|
{
|
|
fault.SetFault(e.FaultCode, e.FaultString);
|
|
responses.Add(fault.Value);
|
|
}
|
|
catch (Exception e2)
|
|
{
|
|
fault.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR,
|
|
XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message);
|
|
responses.Add(fault.Value);
|
|
}
|
|
}
|
|
|
|
return responses;
|
|
}
|
|
|
|
}
|
|
}
|
|
|