WIP
parent
7c0987e424
commit
e94811994f
|
@ -18,279 +18,40 @@ using System.Runtime.CompilerServices;
|
|||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics;
|
||||
using ln.http.resources.reflection;
|
||||
namespace ln.http.resources
|
||||
{
|
||||
public abstract class ReflectiveResource : Resource
|
||||
public class ReflectiveResource : Resource
|
||||
{
|
||||
public override bool HandlesDispatching => true;
|
||||
|
||||
|
||||
Dictionary<string, MethodInfo[]> callableMethods = new Dictionary<string, MethodInfo[]>();
|
||||
Dictionary<string, PropertyInfo> properties = new Dictionary<string, PropertyInfo>();
|
||||
|
||||
Dictionary<string, ReflectiveResource> reflectiveResources = new Dictionary<string, ReflectiveResource>();
|
||||
Reflector reflector;
|
||||
Object o;
|
||||
|
||||
public ReflectiveResource(Resource container, string name,object o)
|
||||
: base(container, name)
|
||||
{
|
||||
initialize();
|
||||
this.o = o;
|
||||
this.reflector = Reflector.GetReflector(o.GetType());
|
||||
}
|
||||
|
||||
private void initialize()
|
||||
public override HttpResponse GetResponse(HttpRequest httpRequest,Queue<string> pathStack)
|
||||
{
|
||||
Dictionary<string, List<MethodInfo>> callables = new Dictionary<string, List<MethodInfo>>();
|
||||
object currentValue = reflector.InvokeRequest(httpRequest, pathStack, this.o);
|
||||
String jsonValue = JsonConvert.SerializeObject(currentValue);
|
||||
|
||||
foreach (MethodInfo methodInfo in this.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
CallableAttribute callableAttribute = methodInfo.GetCustomAttribute<CallableAttribute>();
|
||||
if (callableAttribute != null)
|
||||
{
|
||||
string alias = callableAttribute.Alias == null ? methodInfo.Name : callableAttribute.Alias;
|
||||
if (!callables.ContainsKey(alias))
|
||||
{
|
||||
callables.Add(alias, new List<MethodInfo>());
|
||||
}
|
||||
callables[alias].Add(methodInfo);
|
||||
}
|
||||
}
|
||||
HttpResponse response = new HttpResponse(httpRequest);
|
||||
|
||||
foreach (String alias in callables.Keys)
|
||||
{
|
||||
callableMethods.Add(alias, callables[alias].ToArray());
|
||||
new MethodResource(this, alias);
|
||||
}
|
||||
|
||||
foreach (PropertyInfo propertyInfo in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
if (propertyInfo.GetCustomAttribute<CallableAttribute>() != null)
|
||||
{
|
||||
new CallableProperty(this, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private object InvokeMethodCall(string methodName, object[] arguments)
|
||||
{
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
MethodInfo methodInfo = FindMethodSignature(methodName, arguments.Length);
|
||||
object result = methodInfo.Invoke(this, arguments);
|
||||
|
||||
stopwatch.Stop();
|
||||
Logging.Log(LogLevel.DEBUGDETAIL,"InvokeMethodCall({0},...): {1}ms",methodName,stopwatch.ElapsedMilliseconds);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private object InvokeMethodCall(string methodName, KeyValuePair<string,object>[] arguments)
|
||||
{
|
||||
MethodInfo methodInfo = FindMethodSignature(methodName, arguments.Select((kvp) => kvp.Key).ToArray());
|
||||
Dictionary<string, object> args = new Dictionary<string, object>();
|
||||
foreach (KeyValuePair<string,object> kvp in arguments)
|
||||
args.Add(kvp.Key, kvp.Value);
|
||||
|
||||
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
||||
object[] pl = new object[parameterInfos.Length];
|
||||
|
||||
for (int n=0;n<parameterInfos.Length;n++)
|
||||
pl[n] = args[parameterInfos[n].Name];
|
||||
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
object result = methodInfo.Invoke(this, pl);
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
Logging.Log(LogLevel.DEBUGDETAIL, "InvokeMethodCall({0},...): {1}ms", methodName, stopwatch.ElapsedMilliseconds);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private MethodInfo FindMethodSignature(String methodName,int nParameters)
|
||||
{
|
||||
foreach (MethodInfo methodInfo in callableMethods[methodName])
|
||||
{
|
||||
if (nParameters == methodInfo.GetParameters().Length)
|
||||
{
|
||||
return methodInfo;
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("No method signature matching the parameters was found");
|
||||
}
|
||||
|
||||
private MethodInfo FindMethodSignature(String methodName, String[] argumentNames)
|
||||
{
|
||||
foreach (MethodInfo methodInfo in callableMethods[methodName])
|
||||
{
|
||||
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
||||
if (parameterInfos.Length == argumentNames.Length)
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < parameterInfos.Length; n++)
|
||||
{
|
||||
if (!argumentNames.Contains(parameterInfos[n].Name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == argumentNames.Length)
|
||||
return methodInfo;
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("No method signature matching the parameters was found");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override HttpResponse GetResponse(HttpRequest httpRequest)
|
||||
{
|
||||
if (!httpRequest.GetRequestHeader("Content-Type").Equals("application/json"))
|
||||
{
|
||||
throw new HttpException("JSON Method call failed, call object not received");
|
||||
}
|
||||
|
||||
MethodCall methodCall = JsonConvert.DeserializeObject<MethodCall>(httpRequest.ContentReader.ReadToEnd());
|
||||
MethodResult methodResult;
|
||||
|
||||
try
|
||||
{
|
||||
methodResult = new MethodResult();
|
||||
methodResult.MethodName = methodCall.MethodName;
|
||||
|
||||
methodResult.Result = InvokeMethodCall(methodCall.MethodName, methodCall.Parameters);
|
||||
} catch (Exception e)
|
||||
{
|
||||
methodResult = new MethodResult();
|
||||
methodResult.MethodName = methodCall.MethodName;
|
||||
methodResult.Exception = e;
|
||||
Logging.Log(LogLevel.ERROR, "JsonCallResource: method call caught exception: {0}",e);
|
||||
Logging.Log(e);
|
||||
}
|
||||
|
||||
String result = JsonConvert.SerializeObject(methodResult);
|
||||
|
||||
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
||||
httpResponse.SetHeader("content-type", "application/json");
|
||||
httpResponse.ContentWriter.Write(result);
|
||||
|
||||
return httpResponse;
|
||||
}
|
||||
|
||||
private string SerializeResult(MethodInfo methodInfo,MethodResult methodResult)
|
||||
{
|
||||
if (methodResult.Exception != null)
|
||||
{
|
||||
return JsonConvert.SerializeObject(methodResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FlatSerializeResult(methodResult);
|
||||
}
|
||||
}
|
||||
|
||||
private string FlatSerializeResult(MethodResult methodResult)
|
||||
{
|
||||
JObject jMethodResult = new JObject();
|
||||
jMethodResult.Add("Exception", null);
|
||||
jMethodResult.Add("MethodName", methodResult.MethodName);
|
||||
|
||||
JObject jResult = new JObject();
|
||||
jMethodResult.Add("Result", jResult);
|
||||
|
||||
Type type = methodResult.GetType();
|
||||
|
||||
foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
Type fType = fieldInfo.FieldType;
|
||||
|
||||
if ((fType.IsValueType) || (typeof(string).Equals(fType)) || (fType.IsArray))
|
||||
{
|
||||
jResult.Add(fieldInfo.Name, new JValue(fieldInfo.GetValue(methodResult.Result)));
|
||||
}
|
||||
else
|
||||
{
|
||||
jResult.Add(fieldInfo.Name, new JValue(fieldInfo.GetValue(methodResult.Result).ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
return jMethodResult.ToString();
|
||||
}
|
||||
|
||||
|
||||
class MethodCall
|
||||
{
|
||||
public String MethodName;
|
||||
public object[] Parameters;
|
||||
}
|
||||
|
||||
class MethodResult
|
||||
{
|
||||
public String MethodName;
|
||||
public object Result;
|
||||
public Exception Exception;
|
||||
}
|
||||
|
||||
|
||||
class MethodResource : Resource
|
||||
{
|
||||
public MethodResource(JsonCallResource container,String methodName)
|
||||
:base(container,methodName)
|
||||
{
|
||||
}
|
||||
|
||||
public override HttpResponse GetResponse(HttpRequest httpRequest)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public override void AddResource(Resource resource) => throw new NotImplementedException();
|
||||
public override bool Contains(string name) => throw new NotImplementedException();
|
||||
public override IEnumerable<Resource> GetResources() => throw new NotImplementedException();
|
||||
public override void RemoveResource(Resource resource) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
class CallableProperty : Resource
|
||||
{
|
||||
public CallableProperty(JsonCallResource container, PropertyInfo propertyInfo)
|
||||
: base(container, propertyInfo.Name)
|
||||
{
|
||||
this.propertyInfo = propertyInfo;
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo;
|
||||
|
||||
public override void AddResource(Resource resource) => throw new NotImplementedException();
|
||||
public override void RemoveResource(Resource resource) => throw new NotImplementedException();
|
||||
|
||||
public override bool Contains(string name) => false;
|
||||
public override IEnumerable<Resource> GetResources() => new Resource[0];
|
||||
|
||||
public override HttpResponse GetResponse(HttpRequest httpRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
object v = propertyInfo.GetValue(Container);
|
||||
String result = JsonConvert.SerializeObject(v);
|
||||
|
||||
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
||||
httpResponse.SetHeader("content-type", "application/json");
|
||||
httpResponse.ContentWriter.Write(result);
|
||||
|
||||
return httpResponse;
|
||||
} catch (Exception e)
|
||||
{
|
||||
Logging.Log(e);
|
||||
|
||||
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
||||
httpResponse.StatusCode = 500;
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
response.SetHeader("content-type", "application/json");
|
||||
response.ContentWriter.Write(jsonValue);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override bool Contains(string name) => throw new NotImplementedException();
|
||||
public override void AddResource(Resource resource) => throw new NotImplementedException();
|
||||
public override void RemoveResource(Resource resource) => throw new NotImplementedException();
|
||||
public override IEnumerable<Resource> GetResources() => throw new NotImplementedException();
|
||||
public override HttpResponse GetResponse(HttpRequest httpRequest) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,10 @@ namespace ln.http.resources
|
|||
return this.GetResources().Where((x) => resourceType.IsInstanceOfType(x));
|
||||
}
|
||||
|
||||
public virtual HttpResponse GetResponse(HttpRequest httpRequest, Queue<string> pathStack)
|
||||
{
|
||||
return GetResponse(httpRequest);
|
||||
}
|
||||
public abstract HttpResponse GetResponse(HttpRequest httpRequest);
|
||||
|
||||
private List<Resource> ResourcePathList
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace ln.http.resources
|
|||
Resource currentResource = RootResource;
|
||||
Queue<String> pathStack = new Queue<String>(httpRequest.URI.AbsolutePath.Split(new String[] { "/" }, StringSplitOptions.RemoveEmptyEntries));
|
||||
|
||||
while (pathStack.Count > 0)
|
||||
while ((pathStack.Count > 0) && (!currentResource.HandlesDispatching))
|
||||
{
|
||||
String next = pathStack.Dequeue();
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace ln.http.resources
|
|||
/* TODO: Implement Authorization hook here... */
|
||||
}
|
||||
|
||||
return currentResource.GetResponse(httpRequest);
|
||||
return currentResource.GetResponse(httpRequest,pathStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using ln.http.exceptions;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
namespace ln.http.resources.reflection
|
||||
{
|
||||
public class Reflector
|
||||
|
@ -35,16 +38,66 @@ namespace ln.http.resources.reflection
|
|||
|
||||
}
|
||||
|
||||
public object InvokeRequest(HttpRequest httpRequest,Queue<string> pathStack,object currentValue)
|
||||
public object InvokeRequest(HttpRequest httpRequest, Queue<string> pathStack, object currentValue)
|
||||
{
|
||||
if (pathStack.Count == 0)
|
||||
return currentValue;
|
||||
|
||||
throw new NotImplementedException();
|
||||
StringReader reader = new StringReader(pathStack.Dequeue());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (IsValidNameChar((char)reader.Peek()))
|
||||
sb.Append((char)reader.Read());
|
||||
|
||||
string next = sb.ToString();
|
||||
sb.Clear();
|
||||
|
||||
if (properties.ContainsKey(next))
|
||||
{
|
||||
currentValue = properties[next].GetValue(currentValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new KeyNotFoundException(next);
|
||||
}
|
||||
|
||||
while (reader.Peek() != -1)
|
||||
{
|
||||
char nc = (char)reader.Read();
|
||||
switch (nc)
|
||||
{
|
||||
case '[':
|
||||
while ((reader.Peek() != ']') && (reader.Peek() != -1))
|
||||
sb.Append((char)reader.Read());
|
||||
reader.Read();
|
||||
|
||||
string index = sb.ToString();
|
||||
sb.Clear();
|
||||
|
||||
Type currentType = currentValue.GetType();
|
||||
if (currentType.IsArray)
|
||||
{
|
||||
int i = int.Parse(index);
|
||||
currentValue = ((Array)currentValue).GetValue(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static bool IsValidNameChar(char ch)
|
||||
{
|
||||
return Char.IsLetterOrDigit(ch) | (ch == '_');
|
||||
}
|
||||
|
||||
|
||||
static Dictionary<Type, Reflector> reflectorsCache = new Dictionary<Type, Reflector>();
|
||||
|
|
Loading…
Reference in New Issue