/*
* Copyright (c) Contributors, http://www.openmetaverse.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSim Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.Reflection;
/// XML-RPC System object implementation of extended specifications.
[XmlRpcExposed]
public class XmlRpcSystemObject
{
private XmlRpcServer _server;
static private IDictionary _methodHelp = new Hashtable();
/// Static IDictionary to hold mappings of method name to associated documentation String
static public IDictionary MethodHelp
{
get { return _methodHelp; }
}
/// Constructor.
/// XmlRpcServer server to be the system object for.
public XmlRpcSystemObject(XmlRpcServer server)
{
_server = server;
server.Add("system", this);
_methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description.");
}
/// Invoke a method on a given object.
/// Using reflection, and respecting the XmlRpcExposed attribute,
/// invoke the methodName method on the target
/// instance with the parameters provided. All this packages other Invoke methods
/// end up calling this.
/// Object the value the invoked method returns.
/// If method does not exist, is not exposed, parameters invalid, or invocation
/// results in an exception. Note, the XmlRpcException.Code will indicate cause.
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);
}
}
/// List methods available on all handlers of this server.
/// IList An array of Strings, each String will have form "object.method".
[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;
}
/// Given a method name return the possible signatures for it.
/// String The object.method name to look up.
/// IList Of arrays of signatures.
[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;
}
/// Help for given method signature. Not implemented yet.
/// String The object.method name to look up.
/// String help text. Rich HTML text.
[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;
}
/// Boxcarring support method.
/// IList of calls
/// ArrayList of results/faults.
[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;
}
}
}