From 261d41b68f1e96b73755184fe2a5785ec58c2e5f Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Sat, 4 Jun 2016 00:49:55 -0300 Subject: [PATCH 1/3] Added REST Proxy * Added a REST Proxy that caches all the methods and parameter converters. In this way we don't need to reflect everytime we call a endpoint. --- ASAttrib/ASRest.csproj | 1 + ASAttrib/Attributes/HTTPMethod.cs | 10 +- ASAttrib/Models/RestCall.cs | 7 +- ASAttrib/Processors/RestProcessor.cs | 90 ++------------- ASAttrib/Proxy/RestProxy.cs | 157 ++++++++++++++++++++++++++ ASTools/AS/ASApp.cs | 11 -- ASTools/ASTools.csproj | 2 - ASTools/Class1.cs | 12 -- ASTools/Logger/TextTools.cs | 6 +- AppServer/AppServer.csproj | 1 - AppServer/Models/TestModel.cs | 13 --- AppServer/Program.cs | 11 +- AppServer/Server/ASRunner.cs | 18 +-- AppServer/Server/AppAction.cs | 2 +- AppServer/Server/ApplicationLoader.cs | 21 ++-- AppServer/Server/HttpServer.cs | 3 +- AppServer/Server/LoaderWorker.cs | 4 - 17 files changed, 203 insertions(+), 166 deletions(-) create mode 100644 ASAttrib/Proxy/RestProxy.cs delete mode 100644 ASTools/AS/ASApp.cs delete mode 100644 ASTools/Class1.cs delete mode 100644 AppServer/Models/TestModel.cs diff --git a/ASAttrib/ASRest.csproj b/ASAttrib/ASRest.csproj index bc705db..4cf39c0 100644 --- a/ASAttrib/ASRest.csproj +++ b/ASAttrib/ASRest.csproj @@ -78,6 +78,7 @@ + diff --git a/ASAttrib/Attributes/HTTPMethod.cs b/ASAttrib/Attributes/HTTPMethod.cs index 80b7601..9dae8f7 100644 --- a/ASAttrib/Attributes/HTTPMethod.cs +++ b/ASAttrib/Attributes/HTTPMethod.cs @@ -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; diff --git a/ASAttrib/Models/RestCall.cs b/ASAttrib/Models/RestCall.cs index c1edd03..2ecfc39 100644 --- a/ASAttrib/Models/RestCall.cs +++ b/ASAttrib/Models/RestCall.cs @@ -1,4 +1,5 @@ using ASAttrib.Attributes; +using ASAttrib.Proxy; using System; using System.Collections.Generic; using System.Linq; @@ -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; } diff --git a/ASAttrib/Processors/RestProcessor.cs b/ASAttrib/Processors/RestProcessor.cs index e3ed93a..430d975 100644 --- a/ASAttrib/Processors/RestProcessor.cs +++ b/ASAttrib/Processors/RestProcessor.cs @@ -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 { @@ -16,12 +16,12 @@ public class RestProcessor { private readonly Type[] RestTypes = new Type[] { typeof(GET), typeof(POST), typeof(PUT), typeof(DELETE) }; private Dictionary> endpoints; - private Dictionary instances; + private Dictionary proxies; private Dictionary exceptionHandlers; public RestProcessor() { endpoints = new Dictionary>(); - instances = new Dictionary(); + proxies = new Dictionary(); exceptionHandlers = new Dictionary(); } @@ -35,8 +35,8 @@ 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)); + MethodInfo[] methods = tClass.GetMethods(); foreach (var methodInfo in methods) { foreach (Type rt in RestTypes) { @@ -44,8 +44,8 @@ public void init(Assembly runningAssembly, string modulesAssembly) { 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; @@ -79,12 +79,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]; } @@ -102,77 +102,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 { diff --git a/ASAttrib/Proxy/RestProxy.cs b/ASAttrib/Proxy/RestProxy.cs new file mode 100644 index 0000000..3051ade --- /dev/null +++ b/ASAttrib/Proxy/RestProxy.cs @@ -0,0 +1,157 @@ +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 proxyMethods; + private object instance; + private Type classType; + + internal RestProxy(Type restClass) { + instance = Activator.CreateInstance(restClass); + classType = restClass; + Attribute t = restClass.GetCustomAttribute(typeof(REST)); + LOG.i("Creating proxy for " + restClass.Name); + proxyMethods = new Dictionary(); + REST trest = (REST)t; + 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 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 proxyData = proxyMethods[methodName].ProxyData; + object[] callParams = new object[proxyData.Count]; + for (int i=0; i proxyData; + + public MethodInfo Method { + get { return method; } + } + + public List ProxyData { + get { return proxyData; } + } + + public ProxyMethod(MethodInfo method) { + this.method = method; + this.proxyData = new List(); + } + } + + private class ProxyParameterData { + private ProxyParameterRestType restType; + private Type parameterType; + private string lookName; + private Func parse; + + public ProxyParameterRestType RestType { + get { return restType; } + } + + public Type ParameterType { + get { return parameterType; } + } + + public string LookName { + get { return lookName; } + } + + public Func Parse { + get { return parse; } + } + + public ProxyParameterData(ProxyParameterRestType restType, Type parameterType, string lookName, Func parse) { + this.restType = restType; + this.parameterType = parameterType; + this.lookName = lookName; + this.parse = parse; + } + } + + private enum ProxyParameterRestType { + BODY, + PATH, + QUERY + } + } +} diff --git a/ASTools/AS/ASApp.cs b/ASTools/AS/ASApp.cs deleted file mode 100644 index 57ed9cf..0000000 --- a/ASTools/AS/ASApp.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ASTools.AS { - public interface ASApp { - void load(); - } -} diff --git a/ASTools/ASTools.csproj b/ASTools/ASTools.csproj index 7df24ce..926a876 100644 --- a/ASTools/ASTools.csproj +++ b/ASTools/ASTools.csproj @@ -61,8 +61,6 @@ - - diff --git a/ASTools/Class1.cs b/ASTools/Class1.cs deleted file mode 100644 index d4375e8..0000000 --- a/ASTools/Class1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ASTools -{ - public class Class1 - { - } -} diff --git a/ASTools/Logger/TextTools.cs b/ASTools/Logger/TextTools.cs index 0c2370f..eb82051 100644 --- a/ASTools/Logger/TextTools.cs +++ b/ASTools/Logger/TextTools.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ASTools.Logger { - public class TextTools { + internal class TextTools { private static readonly string logFormat = "{0,-19} - {1, -40} - {2, -6}: {3}"; private static readonly string logFormatNewLine = " "; diff --git a/AppServer/AppServer.csproj b/AppServer/AppServer.csproj index 0f32e55..1069209 100644 --- a/AppServer/AppServer.csproj +++ b/AppServer/AppServer.csproj @@ -68,7 +68,6 @@ - diff --git a/AppServer/Models/TestModel.cs b/AppServer/Models/TestModel.cs deleted file mode 100644 index 9078725..0000000 --- a/AppServer/Models/TestModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AppServer.Models { - public class TestModel { - public string name; - public int count; - public string test; - } -} diff --git a/AppServer/Program.cs b/AppServer/Program.cs index 8670248..3c7214d 100644 --- a/AppServer/Program.cs +++ b/AppServer/Program.cs @@ -1,13 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Reflection; -using ASAttrib.Attributes; -using System.Net; -using AppServer.Server; -using ASAttrib.Processors; +using AppServer.Server; using ASTools.Logger; namespace AppServer { diff --git a/AppServer/Server/ASRunner.cs b/AppServer/Server/ASRunner.cs index 13fb22e..47c415c 100644 --- a/AppServer/Server/ASRunner.cs +++ b/AppServer/Server/ASRunner.cs @@ -1,16 +1,11 @@ using ASAttrib.Models; -using ASAttrib.Processors; using ASTools.Logger; using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Reflection; using System.Text; -using System.Threading.Tasks; namespace AppServer.Server { - class ASRunner { + internal class ASRunner { HttpServer server; ApplicationManager appManager; @@ -46,10 +41,17 @@ RestResult processHttpCalls(HttpListenerRequest request) { return appManager.callEndPoint(app, path, method, req); } catch (Exception e) { RestResult result = new RestResult(); - LOG.e("Exception when calling application " + app + " in endpoint " + method + " " + path + "\r\n" + e.InnerException.ToString()); + string exceptionMessage; + if (e.InnerException != null) { // In the rest exceptions the real exception will be at InnerException. + LOG.e("Exception when calling application " + app + " in endpoint " + method + " " + path + "\r\n" + e.InnerException.ToString()); + exceptionMessage = e.InnerException.ToString(); + } else { // But if we got a internal exception at AppServer, it will be in the root. + LOG.e("Exception when calling application " + app + " in endpoint " + method + " " + path + "\r\n" + e.ToString()); + exceptionMessage = e.ToString(); + } result.StatusCode = HttpStatusCode.InternalServerError; result.ContentType = "text/plain"; - result.Result = Encoding.UTF8.GetBytes(e.InnerException.ToString()); + result.Result = Encoding.UTF8.GetBytes(exceptionMessage); return result; } } else { diff --git a/AppServer/Server/AppAction.cs b/AppServer/Server/AppAction.cs index ba4e6a2..3e3b46c 100644 --- a/AppServer/Server/AppAction.cs +++ b/AppServer/Server/AppAction.cs @@ -1,5 +1,5 @@ namespace AppServer.Server { - class AppAction { + internal class AppAction { private string appName; private bool remove; diff --git a/AppServer/Server/ApplicationLoader.cs b/AppServer/Server/ApplicationLoader.cs index 582b13f..71ef251 100644 --- a/AppServer/Server/ApplicationLoader.cs +++ b/AppServer/Server/ApplicationLoader.cs @@ -8,7 +8,7 @@ using System.Timers; namespace AppServer.Server { - public class ApplicationManager { + internal class ApplicationManager { private static Logger LOG = new Logger(typeof(ApplicationManager)); @@ -19,12 +19,16 @@ public class ApplicationManager { private Dictionary appActions; private FileSystemWatcher watcher; + private string appsDir; + private string deployedDir; public ApplicationManager() { this.appDomains = new Dictionary(); this.loaderWorkers = new Dictionary(); this.watcher = new FileSystemWatcher(); this.appActions = new Dictionary(); + this.appsDir = Path.Combine(".", "apps"); + this.deployedDir = Path.Combine(".", "deployed"); scanAndInitialize(); @@ -96,23 +100,26 @@ private LoaderWorker createLoaderWorker(AppDomain domain) { return (LoaderWorker)domain.CreateInstanceAndUnwrap(lwt.Assembly.FullName, lwt.FullName); } private void scanAndInitialize() { - List apps = Directory.GetDirectories(Path.Combine(".", "apps")).Select(a => Path.GetFileName(a)).ToList(); + if (!Directory.Exists(appsDir)) { + Directory.CreateDirectory(appsDir); + } + List apps = Directory.GetDirectories(appsDir).Select(a => Path.GetFileName(a)).ToList(); foreach (string app in apps) { loadApplication(app); } } private void onCreated(object source, FileSystemEventArgs e) { - if (!e.FullPath.Equals(Path.Combine(".", "apps"))) { + if (!e.FullPath.Equals(appsDir)) { scheduleRefreshApplication(appNameFromFolder(e.FullPath), false); } } private void onDeleted(object source, FileSystemEventArgs e) { - if (!e.FullPath.Equals(Path.Combine(".", "apps"))) { + if (!e.FullPath.Equals(appsDir)) { scheduleRefreshApplication(appNameFromFolder(e.FullPath), true); } } private void onChanged(object source, FileSystemEventArgs e) { - if (!e.FullPath.Equals(Path.Combine(".", "apps"))) { + if (!e.FullPath.Equals(appsDir)) { scheduleRefreshApplication(appNameFromFolder(e.FullPath), !(File.Exists(e.FullPath) || Directory.Exists(e.FullPath))); } } @@ -166,9 +173,9 @@ private void loadApplication(string appName) { LoaderWorker lw = createLoaderWorker(domain); - List assemblies = Directory.GetFiles(Path.Combine(".", "apps", appName)).Where(x => x.Contains(".dll")).ToList(); + List assemblies = Directory.GetFiles(Path.Combine(appsDir, appName)).Where(x => x.Contains(".dll")).ToList(); assemblies.ForEach(appAssembly => { - string targetPath = Path.Combine(".", "deployed", appName); + string targetPath = Path.Combine(deployedDir, appName); string targetFile = Path.Combine(targetPath, Path.GetFileName(appAssembly)); string appDebugAssembly = appAssembly.Replace(".dll", ".pdb"); string targetDebugFile = Path.Combine(targetPath, Path.GetFileName(appDebugAssembly)); diff --git a/AppServer/Server/HttpServer.cs b/AppServer/Server/HttpServer.cs index 135bb86..76266ad 100644 --- a/AppServer/Server/HttpServer.cs +++ b/AppServer/Server/HttpServer.cs @@ -1,13 +1,12 @@ using ASAttrib.Models; using ASTools.Logger; -using Newtonsoft.Json.Linq; using System; using System.Linq; using System.Net; using System.Threading; namespace AppServer.Server { - public class HttpServer { + internal class HttpServer { private readonly HttpListener _listener = new HttpListener(); private Func httpProcesser; private string listenUrl; diff --git a/AppServer/Server/LoaderWorker.cs b/AppServer/Server/LoaderWorker.cs index e9de25a..fda57b8 100644 --- a/AppServer/Server/LoaderWorker.cs +++ b/AppServer/Server/LoaderWorker.cs @@ -3,13 +3,9 @@ using ASAttrib.Processors; using ASTools.Logger; using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace AppServer.Server { public class LoaderWorker : MarshalByRefObject { From 766fb1d5d14c372fcf1a8fa9a1b41dfe03b5de76 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Sat, 11 Jun 2016 12:06:22 -0300 Subject: [PATCH 2/3] Added Dependency Injection Attribute --- ASAttrib/{ASRest.csproj => ASAttrib.csproj} | 1 + ASAttrib/Attributes/Inject.cs | 7 +++ ASAttrib/Processors/RestProcessor.cs | 4 +- ASAttrib/Proxy/RestProxy.cs | 21 ++++++- AppServer.sln | 65 ++++++++------------- AppServer/AppServer.csproj | 4 +- README.md | 14 +++++ SampleApp/MyRestSample.cs | 13 +++++ SampleApp/SampleApp.csproj | 5 +- SampleApp/TestProc.cs | 19 ++++++ 10 files changed, 106 insertions(+), 47 deletions(-) rename ASAttrib/{ASRest.csproj => ASAttrib.csproj} (98%) create mode 100644 ASAttrib/Attributes/Inject.cs create mode 100644 SampleApp/TestProc.cs diff --git a/ASAttrib/ASRest.csproj b/ASAttrib/ASAttrib.csproj similarity index 98% rename from ASAttrib/ASRest.csproj rename to ASAttrib/ASAttrib.csproj index 4cf39c0..40165a5 100644 --- a/ASAttrib/ASRest.csproj +++ b/ASAttrib/ASAttrib.csproj @@ -66,6 +66,7 @@ + diff --git a/ASAttrib/Attributes/Inject.cs b/ASAttrib/Attributes/Inject.cs new file mode 100644 index 0000000..12dcb86 --- /dev/null +++ b/ASAttrib/Attributes/Inject.cs @@ -0,0 +1,7 @@ +using System; + +namespace ASAttrib.Attributes { + [AttributeUsage(AttributeTargets.Field)] + public class Inject : Attribute { + } +} diff --git a/ASAttrib/Processors/RestProcessor.cs b/ASAttrib/Processors/RestProcessor.cs index 430d975..e3e432c 100644 --- a/ASAttrib/Processors/RestProcessor.cs +++ b/ASAttrib/Processors/RestProcessor.cs @@ -18,11 +18,13 @@ public class RestProcessor { private Dictionary> endpoints; private Dictionary proxies; private Dictionary exceptionHandlers; + private Dictionary injectables; public RestProcessor() { endpoints = new Dictionary>(); proxies = new Dictionary(); exceptionHandlers = new Dictionary(); + injectables = new Dictionary(); } public void init(Assembly runningAssembly, string modulesAssembly) { @@ -35,7 +37,7 @@ public void init(Assembly runningAssembly, string modulesAssembly) { if (t != null) { LOG.i("Found REST class " + tClass.Name); REST trest = (REST)t; - proxies.Add(tClass.Name, new RestProxy(tClass)); + proxies.Add(tClass.Name, new RestProxy(tClass, injectables)); MethodInfo[] methods = tClass.GetMethods(); foreach (var methodInfo in methods) { diff --git a/ASAttrib/Proxy/RestProxy.cs b/ASAttrib/Proxy/RestProxy.cs index 3051ade..f9d4ae6 100644 --- a/ASAttrib/Proxy/RestProxy.cs +++ b/ASAttrib/Proxy/RestProxy.cs @@ -17,13 +17,32 @@ internal class RestProxy { private object instance; private Type classType; - internal RestProxy(Type restClass) { + internal RestProxy(Type restClass, Dictionary injectables) { instance = Activator.CreateInstance(restClass); classType = restClass; Attribute t = restClass.GetCustomAttribute(typeof(REST)); LOG.i("Creating proxy for " + restClass.Name); proxyMethods = new Dictionary(); 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)); diff --git a/AppServer.sln b/AppServer.sln index 7f15ac1..aec0169 100644 --- a/AppServer.sln +++ b/AppServer.sln @@ -1,11 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -VisualStudioVersion = 14.0.24720.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppServer", "AppServer\AppServer.csproj", "{F08CF63C-4981-4B21-BC6E-297AF3D98BDE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASRest", "ASAttrib\ASRest.csproj", "{A98DE288-9DD0-46E1-854A-9AAB9091547B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASAttrib", "ASAttrib\ASAttrib.csproj", "{A98DE288-9DD0-46E1-854A-9AAB9091547B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASTools", "ASTools\ASTools.csproj", "{47AD25A1-D8E2-4CB8-A111-2B55380F83B1}" EndProject @@ -19,22 +19,6 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|x64.ActiveCfg = Debug|x64 - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|x64.Build.0 = Debug|x64 - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|Any CPU.Build.0 = Release|Any CPU - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|x64.ActiveCfg = Release|x64 - {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|x64.Build.0 = Release|x64 - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|x64.ActiveCfg = Debug|x64 - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|x64.Build.0 = Debug|x64 - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|Any CPU.Build.0 = Release|Any CPU - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|x64.ActiveCfg = Release|x64 - {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|x64.Build.0 = Release|x64 {F08CF63C-4981-4B21-BC6E-297AF3D98BDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F08CF63C-4981-4B21-BC6E-297AF3D98BDE}.Debug|Any CPU.Build.0 = Debug|Any CPU {F08CF63C-4981-4B21-BC6E-297AF3D98BDE}.Debug|x64.ActiveCfg = Debug|x64 @@ -43,6 +27,22 @@ Global {F08CF63C-4981-4B21-BC6E-297AF3D98BDE}.Release|Any CPU.Build.0 = Release|Any CPU {F08CF63C-4981-4B21-BC6E-297AF3D98BDE}.Release|x64.ActiveCfg = Release|x64 {F08CF63C-4981-4B21-BC6E-297AF3D98BDE}.Release|x64.Build.0 = Release|x64 + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|x64.ActiveCfg = Debug|x64 + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Debug|x64.Build.0 = Debug|x64 + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|Any CPU.Build.0 = Release|Any CPU + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|x64.ActiveCfg = Release|x64 + {A98DE288-9DD0-46E1-854A-9AAB9091547B}.Release|x64.Build.0 = Release|x64 + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|x64.ActiveCfg = Debug|x64 + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Debug|x64.Build.0 = Debug|x64 + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|Any CPU.Build.0 = Release|Any CPU + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|x64.ActiveCfg = Release|x64 + {47AD25A1-D8E2-4CB8-A111-2B55380F83B1}.Release|x64.Build.0 = Release|x64 {FBBDCCDB-5F07-4B4E-B7B1-09DE7504A5D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FBBDCCDB-5F07-4B4E-B7B1-09DE7504A5D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {FBBDCCDB-5F07-4B4E-B7B1-09DE7504A5D1}.Debug|x64.ActiveCfg = Debug|x64 @@ -52,9 +52,12 @@ Global {FBBDCCDB-5F07-4B4E-B7B1-09DE7504A5D1}.Release|x64.ActiveCfg = Release|x64 {FBBDCCDB-5F07-4B4E-B7B1-09DE7504A5D1}.Release|x64.Build.0 = Release|x64 EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 - $0.TextStylePolicy = $1 + $0.TextStylePolicy = $4 $1.inheritsSet = null $1.scope = text/x-csharp $0.CSharpFormattingPolicy = $2 @@ -70,14 +73,12 @@ Global $2.inheritsSet = Mono $2.inheritsScope = text/x-csharp $2.scope = text/x-csharp - $0.TextStylePolicy = $3 $3.FileWidth = 120 $3.TabWidth = 2 $3.IndentWidth = 2 $3.inheritsSet = VisualStudio $3.inheritsScope = text/plain $3.scope = text/plain - $0.TextStylePolicy = $4 $4.inheritsSet = null $4.scope = application/xml $0.XmlFormattingPolicy = $5 @@ -89,21 +90,19 @@ Global $6.IncludeInNewFiles = True $0.NameConventionPolicy = $7 $7.Rules = $8 - $8.NamingRule = $9 + $8.NamingRule = $28 $9.Name = Namespaces $9.AffectedEntity = Namespace $9.VisibilityMask = VisibilityMask $9.NamingStyle = PascalCase $9.IncludeInstanceMembers = True $9.IncludeStaticEntities = True - $8.NamingRule = $10 $10.Name = Types $10.AffectedEntity = Class, Struct, Enum, Delegate $10.VisibilityMask = Public $10.NamingStyle = PascalCase $10.IncludeInstanceMembers = True $10.IncludeStaticEntities = True - $8.NamingRule = $11 $11.Name = Interfaces $11.RequiredPrefixes = $12 $12.String = I @@ -112,7 +111,6 @@ Global $11.NamingStyle = PascalCase $11.IncludeInstanceMembers = True $11.IncludeStaticEntities = True - $8.NamingRule = $13 $13.Name = Attributes $13.RequiredSuffixes = $14 $14.String = Attribute @@ -121,7 +119,6 @@ Global $13.NamingStyle = PascalCase $13.IncludeInstanceMembers = True $13.IncludeStaticEntities = True - $8.NamingRule = $15 $15.Name = Event Arguments $15.RequiredSuffixes = $16 $16.String = EventArgs @@ -130,7 +127,6 @@ Global $15.NamingStyle = PascalCase $15.IncludeInstanceMembers = True $15.IncludeStaticEntities = True - $8.NamingRule = $17 $17.Name = Exceptions $17.RequiredSuffixes = $18 $18.String = Exception @@ -139,70 +135,60 @@ Global $17.NamingStyle = PascalCase $17.IncludeInstanceMembers = True $17.IncludeStaticEntities = True - $8.NamingRule = $19 $19.Name = Methods $19.AffectedEntity = Methods $19.VisibilityMask = Protected, Public $19.NamingStyle = PascalCase $19.IncludeInstanceMembers = True $19.IncludeStaticEntities = True - $8.NamingRule = $20 $20.Name = Static Readonly Fields $20.AffectedEntity = ReadonlyField $20.VisibilityMask = Protected, Public $20.NamingStyle = PascalCase $20.IncludeInstanceMembers = False $20.IncludeStaticEntities = True - $8.NamingRule = $21 $21.Name = Fields $21.AffectedEntity = Field $21.VisibilityMask = Protected, Public $21.NamingStyle = PascalCase $21.IncludeInstanceMembers = True $21.IncludeStaticEntities = True - $8.NamingRule = $22 $22.Name = ReadOnly Fields $22.AffectedEntity = ReadonlyField $22.VisibilityMask = Protected, Public $22.NamingStyle = PascalCase $22.IncludeInstanceMembers = True $22.IncludeStaticEntities = False - $8.NamingRule = $23 $23.Name = Constant Fields $23.AffectedEntity = ConstantField $23.VisibilityMask = Protected, Public $23.NamingStyle = PascalCase $23.IncludeInstanceMembers = True $23.IncludeStaticEntities = True - $8.NamingRule = $24 $24.Name = Properties $24.AffectedEntity = Property $24.VisibilityMask = Protected, Public $24.NamingStyle = PascalCase $24.IncludeInstanceMembers = True $24.IncludeStaticEntities = True - $8.NamingRule = $25 $25.Name = Events $25.AffectedEntity = Event $25.VisibilityMask = Protected, Public $25.NamingStyle = PascalCase $25.IncludeInstanceMembers = True $25.IncludeStaticEntities = True - $8.NamingRule = $26 $26.Name = Enum Members $26.AffectedEntity = EnumMember $26.VisibilityMask = VisibilityMask $26.NamingStyle = PascalCase $26.IncludeInstanceMembers = True $26.IncludeStaticEntities = True - $8.NamingRule = $27 $27.Name = Parameters $27.AffectedEntity = Parameter $27.VisibilityMask = VisibilityMask $27.NamingStyle = CamelCase $27.IncludeInstanceMembers = True $27.IncludeStaticEntities = True - $8.NamingRule = $28 $28.Name = Type Parameters $28.RequiredPrefixes = $29 $29.String = T @@ -212,7 +198,4 @@ Global $28.IncludeInstanceMembers = True $28.IncludeStaticEntities = True EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection EndGlobal diff --git a/AppServer/AppServer.csproj b/AppServer/AppServer.csproj index 1069209..08ededa 100644 --- a/AppServer/AppServer.csproj +++ b/AppServer/AppServer.csproj @@ -81,9 +81,9 @@ - + {A98DE288-9DD0-46E1-854A-9AAB9091547B} - ASRest + ASAttrib {47AD25A1-D8E2-4CB8-A111-2B55380F83B1} diff --git a/README.md b/README.md index 4b6c48a..1dc157c 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ So far we have: * JSON Serializer for Object Return in REST calls * **Custom Exception Handlers** * Argument Deserialization for REST calls +* Dependency Injection with **Inject** attribute TODO ====== @@ -48,6 +49,19 @@ The SampleApp Example: namespace SampleApp { [Rest("/hue")] public class MyRestSample { + [Inject] + private TestProc myInjectedProc; + + [GET("/inject-test")] + public string injTest() { + return myInjectedProc.myName(); + } + + [POST("/inject-test")] + public TestModel injTestPost(TestModel model) { + return myInjectedProc.addCount(model, 20); + } + [GET("/test")] public string hueTest([QueryParam] string param0, [QueryParam] float param1) { return "GET TO HUEHUE with param: Param0(" + param0 + "), Param1(" + param1 +")"; diff --git a/SampleApp/MyRestSample.cs b/SampleApp/MyRestSample.cs index cfe259c..63df686 100644 --- a/SampleApp/MyRestSample.cs +++ b/SampleApp/MyRestSample.cs @@ -6,11 +6,24 @@ namespace SampleApp { [REST("/hue")] public class MyRestSample { + [Inject] + private TestProc myInjectedProc; + [GET("/test")] public string hueTest([QueryParam] string param0, [QueryParam] float param1) { return "GET TO HUEHUE with param: Param0(" + param0 + "), Param1(" + param1 +")"; } + [GET("/inject-test")] + public string injTest() { + return myInjectedProc.myName(); + } + + [POST("/inject-test")] + public TestModel injTestPost(TestModel model) { + return myInjectedProc.addCount(model, 20); + } + [POST("/test")] public TestModel hueTest2(TestModel model) { model.count += 100; diff --git a/SampleApp/SampleApp.csproj b/SampleApp/SampleApp.csproj index 9b2789f..cddaa7b 100644 --- a/SampleApp/SampleApp.csproj +++ b/SampleApp/SampleApp.csproj @@ -66,11 +66,12 @@ + - + {A98DE288-9DD0-46E1-854A-9AAB9091547B} - ASRest + ASAttrib {47AD25A1-D8E2-4CB8-A111-2B55380F83B1} diff --git a/SampleApp/TestProc.cs b/SampleApp/TestProc.cs new file mode 100644 index 0000000..eaecf62 --- /dev/null +++ b/SampleApp/TestProc.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SampleApp { + public class TestProc { + + public string myName() { + return typeof(TestProc).Name; + } + + public TestModel addCount(TestModel t, int c) { + t.count += c; + return t; + } + } +} From 2e450827004d9bc2a7e9a06b1d670a32897e109b Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Fri, 17 Jun 2016 23:45:39 -0300 Subject: [PATCH 3/3] Added Configuration file support --- AppServer/AppConfig.xml | 6 ++++ AppServer/AppServer.csproj | 6 ++++ AppServer/Program.cs | 17 ++++++++-- AppServer/Server/ASRunner.cs | 8 +++++ AppServer/Server/ServerConfig.cs | 54 ++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 AppServer/AppConfig.xml create mode 100644 AppServer/Server/ServerConfig.cs diff --git a/AppServer/AppConfig.xml b/AppServer/AppConfig.xml new file mode 100644 index 0000000..7984358 --- /dev/null +++ b/AppServer/AppConfig.xml @@ -0,0 +1,6 @@ + + + http://localhost:8080/ + DEBUG + . + \ No newline at end of file diff --git a/AppServer/AppServer.csproj b/AppServer/AppServer.csproj index 08ededa..826d922 100644 --- a/AppServer/AppServer.csproj +++ b/AppServer/AppServer.csproj @@ -75,6 +75,7 @@ + @@ -90,6 +91,11 @@ ASTools + + + Always + +