Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/racerxdl/AppServer
Browse files Browse the repository at this point in the history
  • Loading branch information
racerxdl committed Jul 24, 2016
2 parents d1e55cb + 2e45082 commit 99c8320
Show file tree
Hide file tree
Showing 25 changed files with 397 additions and 214 deletions.
2 changes: 2 additions & 0 deletions ASAttrib/ASRest.csproj → ASAttrib/ASAttrib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<ItemGroup>
<Compile Include="Attributes\DELETE.cs" />
<Compile Include="Attributes\HTTPMethod.cs" />
<Compile Include="Attributes\Inject.cs" />
<Compile Include="Attributes\PathParam.cs" />
<Compile Include="Attributes\POST.cs" />
<Compile Include="Attributes\GET.cs" />
Expand All @@ -78,6 +79,7 @@
<Compile Include="Models\RestRequest.cs" />
<Compile Include="Models\RestResult.cs" />
<Compile Include="Processors\RestProcessor.cs" />
<Compile Include="Proxy\RestProxy.cs" />
<Compile Include="Processors\Tools.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Interfaces\IRestExceptionHandler.cs" />
Expand Down
10 changes: 2 additions & 8 deletions ASAttrib/Attributes/HTTPMethod.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ASAttrib.Attributes {
public interface HTTPMethod {
namespace ASAttrib.Attributes {
internal interface HTTPMethod {

string Path {
get;
Expand Down
7 changes: 7 additions & 0 deletions ASAttrib/Attributes/Inject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System;

namespace ASAttrib.Attributes {
[AttributeUsage(AttributeTargets.Field)]
public class Inject : Attribute {
}
}
7 changes: 4 additions & 3 deletions ASAttrib/Models/RestCall.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ASAttrib.Attributes;
using ASAttrib.Proxy;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -8,9 +9,9 @@
using System.Threading.Tasks;

namespace ASAttrib.Models {
public class RestCall {
public Type methodClass;
public MethodInfo call;
internal class RestCall {
public string className;
public string methodName;
public HTTPMethod method;
public REST baseRest;
}
Expand Down
92 changes: 13 additions & 79 deletions ASAttrib/Processors/RestProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Linq;
using ASAttrib.Proxy;

namespace ASAttrib.Processors {
public class RestProcessor {
Expand All @@ -16,13 +16,15 @@ public class RestProcessor {
private readonly Type[] RestTypes = new Type[] { typeof(GET), typeof(POST), typeof(PUT), typeof(DELETE) };

private Dictionary<string, Dictionary<string, RestCall>> endpoints;
private Dictionary<string, object> instances;
private Dictionary<string, RestProxy> proxies;
private Dictionary<string, IRestExceptionHandler> exceptionHandlers;
private Dictionary<string, Object> injectables;

public RestProcessor() {
endpoints = new Dictionary<string, Dictionary<string, RestCall>>();
instances = new Dictionary<string, object>();
proxies = new Dictionary<string, RestProxy>();
exceptionHandlers = new Dictionary<string, IRestExceptionHandler>();
injectables = new Dictionary<string, object>();
}

public void init(Assembly runningAssembly, string modulesAssembly) {
Expand All @@ -35,17 +37,17 @@ public void init(Assembly runningAssembly, string modulesAssembly) {
if (t != null) {
LOG.i("Found REST class " + tClass.Name);
REST trest = (REST)t;
object instance = Activator.CreateInstance(tClass);
instances.Add(tClass.Name, instance);
proxies.Add(tClass.Name, new RestProxy(tClass, injectables));

MethodInfo[] methods = tClass.GetMethods();
foreach (var methodInfo in methods) {
foreach (Type rt in RestTypes) {
Attribute rta = methodInfo.GetCustomAttribute(rt);
if (rta != null) {
RestCall restCall = new RestCall();
try {
restCall.methodClass = tClass;
restCall.call = methodInfo;
restCall.className = tClass.Name;
restCall.methodName = methodInfo.Name;
restCall.method = (HTTPMethod)rta;
restCall.baseRest = trest;

Expand Down Expand Up @@ -79,12 +81,12 @@ public void init(Assembly runningAssembly, string modulesAssembly) {
}
}

LOG.i("Initialized " + instances.Count + " REST instances.");
LOG.i("Initialized " + proxies.Count + " REST proxies.");
LOG.i("Initialized " + endpoints.Keys.Count + " REST endpoints.");
LOG.i("Initialized " + exceptionHandlers.Keys.Count + " Custom Exception Handlers");
}

public RestCall getEndPoint(string path, string method) {
private RestCall getEndPoint(string path, string method) {
if (endpoints.ContainsKey(path) && endpoints[path].ContainsKey(method)) {
return endpoints[path][method];
}
Expand All @@ -102,77 +104,9 @@ public IRestExceptionHandler getExceptionHandler(string exceptionName) {

public RestResult callEndPoint(string path, string method, RestRequest request) {
RestCall rc = getEndPoint(path, method);
if (instances.ContainsKey(rc.methodClass.Name)) {
/**
* Parameters Processing
*
* For now we're using Reflection to fill the parameters,
* but in the future I think it is best to create a proxy class
* to the target class, that receives RestRequest and calls the
* subsequent class with the correct parameters. Then we only
* do reflection at the setup
*/
ParameterInfo[] parameters = rc.call.GetParameters();
object[] methodParams = new object[parameters.Length];
int i = 0;
foreach (ParameterInfo param in parameters) {
// Try query param
Attribute pa = param.GetCustomAttribute(typeof(QueryParam));
if (pa != null) {
QueryParam q = (QueryParam)pa;
string queryName = q.ParamName == null ? param.Name : q.ParamName;
string queryData = request.QueryString[queryName];

// If anyone knows a better way to do this. Please say it.
// This is ugly
if (!typeof(string).IsAssignableFrom(param.ParameterType)) {
// Not String Parameter
if (typeof(long).IsAssignableFrom(param.ParameterType)) {
long output = 0;
long.TryParse(queryData, out output);
methodParams[i] = output;
} else if (typeof(int).IsAssignableFrom(param.ParameterType)) {
int output = 0;
int.TryParse(queryData, out output);
methodParams[i] = output;
} else if (typeof(double).IsAssignableFrom(param.ParameterType)) {
double output = 0;
double.TryParse(queryData, out output);
methodParams[i] = output;
} else if (typeof(float).IsAssignableFrom(param.ParameterType)) {
float output = 0;
float.TryParse(queryData, out output);
methodParams[i] = output;
}
} else {
methodParams[i] = queryData;
}

i++;
continue;
}

// Try Path param
pa = param.GetCustomAttribute(typeof(PathParam));
if (pa != null) {
// TODO: Path Param
i++;
continue;
}

// If no match to our Attributes, then it is a body data. Try to deserialize
if (request.BodyData != null) {
if (typeof(string).IsAssignableFrom(param.ParameterType)) {
methodParams[i] = request.BodyData;
} else {
methodParams[i] = JsonConvert.DeserializeObject(request.BodyData, param.ParameterType);
}
}
i++;
}
if (proxies.ContainsKey(rc.className)) {
object ret = proxies[rc.className].callMethod(rc.methodName, request);

// Calling the target method
object ret = rc.call.Invoke(instances[rc.methodClass.Name], methodParams);
if (ret is string) {
return new RestResult((string)ret);
} else {
Expand Down
176 changes: 176 additions & 0 deletions ASAttrib/Proxy/RestProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using ASAttrib.Attributes;
using ASAttrib.Models;
using ASTools.Logger;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ASAttrib.Proxy {
internal class RestProxy {
private static readonly Type[] baseTypes = { typeof(int), typeof(float), typeof(long), typeof(double) };

private static Logger LOG = new Logger(typeof(RestProxy));

private Dictionary<string, ProxyMethod> proxyMethods;
private object instance;
private Type classType;

internal RestProxy(Type restClass, Dictionary<string, Object> injectables) {
instance = Activator.CreateInstance(restClass);
classType = restClass;
Attribute t = restClass.GetCustomAttribute(typeof(REST));
LOG.i("Creating proxy for " + restClass.Name);
proxyMethods = new Dictionary<string, ProxyMethod>();
REST trest = (REST)t;

// Search Injectables to inject
FieldInfo[] fields = restClass.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields) {
if (field.GetCustomAttribute(typeof(Inject)) != null) {
Type ft = field.FieldType;
Object injectableInstance;
if (injectables.ContainsKey(ft.FullName)) {
injectableInstance = injectables[ft.FullName];
} else {
LOG.i("Creating injectable instance for class " + ft.FullName);
injectableInstance = Activator.CreateInstance(ft);
injectables.Add(ft.Name, injectableInstance);
}
field.SetValue(instance, injectableInstance);
}
}

// Search Methods to Map
MethodInfo[] methods = restClass.GetMethods();
foreach (var methodInfo in methods) {
proxyMethods.Add(methodInfo.Name, new ProxyMethod(methodInfo));
foreach (var paramInfo in methodInfo.GetParameters()) {
// Default to body param
ProxyParameterRestType restType = ProxyParameterRestType.BODY;
string lookName = paramInfo.Name;
Attribute p;

if ( (p = paramInfo.GetCustomAttribute(typeof(QueryParam))) != null) {
restType = ProxyParameterRestType.QUERY;
lookName = ((QueryParam)p).ParamName != null ? ((QueryParam)p).ParamName : paramInfo.Name;
} else if ( (p = paramInfo.GetCustomAttribute(typeof(PathParam))) != null) {
restType = ProxyParameterRestType.PATH;
lookName = ((PathParam)p).ParamName != null ? ((PathParam)p).ParamName : paramInfo.Name;
}

Func<string, object> parser;
Type baseType;

if ((baseType = getBaseType(paramInfo.ParameterType)) != null) {
parser = x => {
object[] dp = new object[] { x, Activator.CreateInstance(baseType) };
baseType.InvokeMember("TryParse", BindingFlags.InvokeMethod, null, null, dp);
return dp[1];
};
} else if (typeof(string).IsAssignableFrom(paramInfo.ParameterType)) {
parser = x => x;
} else {
parser = x => JsonConvert.DeserializeObject(x, paramInfo.ParameterType);
}

proxyMethods[methodInfo.Name].ProxyData.Add(new ProxyParameterData(restType, paramInfo.ParameterType, lookName, parser));
}
}
}

public object callMethod(string methodName, RestRequest request) {
return proxyMethods[methodName].Method.Invoke(instance, paramsBuilder(methodName, request));
}

private static Type getBaseType(Type t) {
try {
return baseTypes.Where(x => x.IsAssignableFrom(t)).ElementAt(0);
} catch (ArgumentOutOfRangeException) {
return null;
}
}

private object[] paramsBuilder(string methodName, RestRequest request) {
List<ProxyParameterData> proxyData = proxyMethods[methodName].ProxyData;
object[] callParams = new object[proxyData.Count];
for (int i=0; i<proxyData.Count; i++) {

string parseData = null;

switch (proxyData[i].RestType) {
case ProxyParameterRestType.BODY:
parseData = request.BodyData;
break;
case ProxyParameterRestType.PATH:
// TODO
break;
case ProxyParameterRestType.QUERY:
parseData = request.QueryString[proxyData[i].LookName];
break;
}

if (parseData != null) {
callParams[i] = proxyData[i].Parse(parseData);
}
}

return callParams;
}

private class ProxyMethod {
private MethodInfo method;
private List<ProxyParameterData> proxyData;

public MethodInfo Method {
get { return method; }
}

public List<ProxyParameterData> ProxyData {
get { return proxyData; }
}

public ProxyMethod(MethodInfo method) {
this.method = method;
this.proxyData = new List<ProxyParameterData>();
}
}

private class ProxyParameterData {
private ProxyParameterRestType restType;
private Type parameterType;
private string lookName;
private Func<string, object> parse;

public ProxyParameterRestType RestType {
get { return restType; }
}

public Type ParameterType {
get { return parameterType; }
}

public string LookName {
get { return lookName; }
}

public Func<string, object> Parse {
get { return parse; }
}

public ProxyParameterData(ProxyParameterRestType restType, Type parameterType, string lookName, Func<string, object> parse) {
this.restType = restType;
this.parameterType = parameterType;
this.lookName = lookName;
this.parse = parse;
}
}

private enum ProxyParameterRestType {
BODY,
PATH,
QUERY
}
}
}
11 changes: 0 additions & 11 deletions ASTools/AS/ASApp.cs

This file was deleted.

2 changes: 0 additions & 2 deletions ASTools/ASTools.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AS\ASApp.cs" />
<Compile Include="Class1.cs" />
<Compile Include="Logger\Logger.cs" />
<Compile Include="Logger\LogManager.cs" />
<Compile Include="Logger\TextTools.cs" />
Expand Down
Loading

0 comments on commit 99c8320

Please sign in to comment.