PageRenderTime 59ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/AJAX/Futures/July 2007 Futures/AtlasBridgePublic/Services/BridgeBuildProvider.cs

#
C# | 309 lines | 203 code | 37 blank | 69 comment | 23 complexity | bf600ed156818de93efc3ee828f055fe MD5 | raw file
Possible License(s): BSD-3-Clause
  1. namespace Microsoft.Web.Preview.Services {
  2. using System;
  3. using System.CodeDom;
  4. using System.CodeDom.Compiler;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.Net;
  9. using System.Reflection;
  10. using System.Web;
  11. using System.Web.Compilation;
  12. using System.Web.Script.Services;
  13. using System.Web.Services;
  14. using Microsoft.Web.Util;
  15. class BridgeBuildProvider : BuildProvider {
  16. // Store the xml push it into the file for reparsing later
  17. private string _bridgeXml = String.Empty;
  18. private BridgeService _service;
  19. protected BridgeService Service {
  20. get {
  21. if (_service == null) {
  22. using (Stream stream = OpenStream(VirtualPath)) {
  23. using (TextReader reader = new StreamReader(stream)) {
  24. _bridgeXml = reader.ReadToEnd();
  25. }
  26. }
  27. if (String.IsNullOrEmpty(_bridgeXml))
  28. throw new InvalidOperationException("ASBX file contents are empty");
  29. _service = new BridgeService(_bridgeXml);
  30. // Update the service cache
  31. BridgeService.UpdateCache(VirtualPath, _service);
  32. }
  33. return _service;
  34. }
  35. }
  36. public BridgeBuildProvider() {
  37. }
  38. public override CompilerType CodeCompilerType {
  39. get {
  40. return GetDefaultCompilerTypeForLanguage(Service.Language);
  41. }
  42. }
  43. public override System.Collections.ICollection VirtualPathDependencies {
  44. get {
  45. List<string> depedencies = new List<string>(2);
  46. depedencies.Add(VirtualPath);
  47. if (!string.IsNullOrEmpty(Service.PartialClassFile))
  48. depedencies.Add(Service.PartialClassFile);
  49. return depedencies;
  50. }
  51. }
  52. /*
  53. * Line 12: namespace Hao.Kung.Samples {
  54. Line 13: using System;
  55. Line 14: using System.Net;
  56. Line 15: using System.Web.Services;
  57. Line 16: using System.Collections;
  58. Line 17: using System.Xml.Serialization;
  59. Line 18: using System.Web.Services;
  60. Line 19:
  61. Line 20:
  62. Line 21: [WebService(Name="http://tempuri.org/")]
  63. Line 22: [WebServiceBinding(ConformsTo=System.Web.Services.WsiProfiles.BasicProfile1_1)]
  64. Line 23: public partial class MSNSearch {
  65. Line 24:
  66. Line 25: public MSNSearch() {
  67. Line 26: this.VirtualPath = "/atlas/BCL/msn.asbx";
  68. this.BridgeXml = <contents of MSN.asbx>
  69. Line 27: }
  70. Line 28: [System.Web.Script.Services.ScriptService()]
  71. Line 29: [System.Web.Services.WebMethodAttribute()]
  72. Line 30: [System.Xml.Serialization.XmlIncludeAttribute(typeof(MSN.SearchRequest))]
  73. Line 31: public object Search(System.Collections.IDictionary args) {
  74. Line 32: return this.Invoke(new System.Web.Services.BridgeRequest("Search", args));
  75. Line 33: }
  76. Line 34:
  77. */
  78. public override void GenerateCode(AssemblyBuilder assemBuilder) {
  79. CodeNamespace ns = new CodeNamespace(Service.Namespace);
  80. ns.Imports.Add(new CodeNamespaceImport("System"));
  81. ns.Imports.Add(new CodeNamespaceImport("System.Net"));
  82. ns.Imports.Add(new CodeNamespaceImport("System.Web.Services"));
  83. ns.Imports.Add(new CodeNamespaceImport("System.Collections"));
  84. ns.Imports.Add(new CodeNamespaceImport("System.Xml.Serialization"));
  85. ns.Imports.Add(new CodeNamespaceImport("Microsoft.Web.Preview.Services"));
  86. ns.Imports.Add(new CodeNamespaceImport("System.Web.Script.Services"));
  87. CodeCompileUnit unit = new CodeCompileUnit();
  88. unit.Namespaces.Add(ns);
  89. CodeTypeDeclaration classType = new CodeTypeDeclaration(Service.Classname);
  90. classType.BaseTypes.Add(typeof(BridgeHandler));
  91. classType.IsPartial = true;
  92. ns.Types.Add(classType);
  93. classType.CustomAttributes.Add(new CodeAttributeDeclaration("ScriptService"));
  94. classType.CustomAttributes.Add(new CodeAttributeDeclaration("WebService", new CodeAttributeArgument("Name", new CodePrimitiveExpression("http://tempuri.org/"))));
  95. classType.CustomAttributes.Add(new CodeAttributeDeclaration("WebServiceBinding", new CodeAttributeArgument("ConformsTo", new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(WsiProfiles)), "BasicProfile1_1"))));
  96. /**
  97. * public ClassName() {
  98. * VirtualPath = <the virtual path>
  99. * }
  100. */
  101. CodeConstructor constructor = new CodeConstructor();
  102. constructor.Attributes = MemberAttributes.Public;
  103. constructor.Statements.Add(new CodeAssignStatement(
  104. new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "VirtualPath"),
  105. new CodePrimitiveExpression(VirtualPath)));
  106. constructor.Statements.Add(new CodeAssignStatement(
  107. new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "BridgeXml"),
  108. new CodePrimitiveExpression(_bridgeXml)));
  109. classType.Members.Add(constructor);
  110. ServiceInfo serviceInfo = Service.ServiceInfo;
  111. foreach (BridgeMethodInfo methodInfo in serviceInfo.Methods.Values) {
  112. classType.Members.Add(GenerateWebMethodCode(methodInfo.Name, methodInfo.XmlIncludes, methodInfo.GetEnabled, methodInfo.ResponseFormat, false));
  113. }
  114. // Skip the proxy method code generation for REST
  115. if (!string.Equals(serviceInfo.ServiceClass, "Microsoft.Web.Preview.Services.BridgeRestProxy"))
  116. classType.Members.Add(GenerateProxyMethodCode());
  117. // BridgeMethod hook
  118. classType.Members.Add(GenerateWebMethodCode("__invokeBridge", null, false, ResponseFormat.Json, true));
  119. assemBuilder.AddCodeCompileUnit(this, unit);
  120. if (!string.IsNullOrEmpty(Service.PartialClassFile)) {
  121. using (TextReader reader = OpenReader(Service.PartialClassFile)) {
  122. CodeSnippetCompileUnit snippet = new CodeSnippetCompileUnit(reader.ReadToEnd());
  123. if (HttpContext.Current != null) {
  124. snippet.LinePragma = new CodeLinePragma(HttpContext.Current.Request.MapPath(Service.PartialClassFile), 1);
  125. }
  126. assemBuilder.AddCodeCompileUnit(this, snippet);
  127. }
  128. }
  129. }
  130. /**
  131. * public virtual object CallServiceClassMethod(string method, Dictionary<string, object> args, ICredentials credentials, string serviceUrl) {
  132. * if (method.Equals("method")) {
  133. * ServiceProxy proxy = new ServiceProxy();
  134. * proxy.Url = serviceUrl (if Proxy has a Url property)
  135. * proxy.Credentials = credentials (if Proxy has a Credentials property)
  136. * object arg = args[paramInfo.ServiceName];
  137. * ArgType1 arg1;
  138. * if (arg is argType1) arg1 = (ArgType1)arg;
  139. * return new Proxy().Method1(arg1, arg2, ...);
  140. */
  141. private CodeMemberMethod GenerateProxyMethodCode() {
  142. CodeMemberMethod method = new CodeMemberMethod();
  143. method.Attributes = MemberAttributes.Override | MemberAttributes.Public;
  144. method.Name = "CallServiceClassMethod";
  145. method.ReturnType = new CodeTypeReference(typeof(object));
  146. method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "method"));
  147. method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Dictionary<String, object>), "args"));
  148. method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(ICredentials), "credentials"));
  149. method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "url"));
  150. // Prevent duplicate methods, as many bridge methods can map to the same server call
  151. Dictionary<string, NGenWrapper<bool>> methodMap = new Dictionary<string, NGenWrapper<bool>>();
  152. Type serviceType = BridgeService.GetType(_service.ServiceInfo.ServiceClass);
  153. foreach (BridgeMethodInfo methodInfo in _service.ServiceInfo.Methods.Values) {
  154. if (methodMap.ContainsKey(methodInfo.ServerName))
  155. continue;
  156. methodMap[methodInfo.ServerName] = true;
  157. MethodInfo serviceMethodInfo = serviceType.GetMethod(methodInfo.ServerName);
  158. if (serviceMethodInfo == null) {
  159. throw new ArgumentException("No such method on service proxy class: " + methodInfo.ServerName);
  160. }
  161. ParameterInfo[] paramData = serviceMethodInfo.GetParameters();
  162. // if (method == "Method1"
  163. CodeConditionStatement ifStmt = new CodeConditionStatement();
  164. ifStmt.Condition = new CodeMethodInvokeExpression(new CodePrimitiveExpression(methodInfo.ServerName), "Equals", new CodeArgumentReferenceExpression("method"));
  165. // <ServiceClass> proxy = new <ServiceClass>()
  166. ifStmt.TrueStatements.Add(new CodeVariableDeclarationStatement(serviceType, "proxy"));
  167. ifStmt.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("proxy"), new CodeObjectCreateExpression(serviceType)));
  168. // check if the proxy class has a Credentials property
  169. PropertyInfo credProp = serviceType.GetProperty("Credentials");
  170. if (credProp != null) {
  171. // proxy.Credentials = credentials;
  172. CodeConditionStatement ifCredNotNull = new CodeConditionStatement();
  173. ifCredNotNull.Condition = new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("credentials"), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
  174. ifCredNotNull.TrueStatements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("proxy"), "Credentials"), new CodeVariableReferenceExpression("credentials")));
  175. ifStmt.TrueStatements.Add(ifCredNotNull);
  176. }
  177. // check if the proxy class has a Url property
  178. PropertyInfo urlProp = serviceType.GetProperty("Url");
  179. if (urlProp != null) {
  180. // proxy.Credentials = credentials;
  181. CodeConditionStatement ifUrlNotEmpty = new CodeConditionStatement();
  182. ifUrlNotEmpty.Condition = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(string)), "IsNullOrEmpty"), new CodeVariableReferenceExpression("url"));
  183. ifUrlNotEmpty.FalseStatements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("proxy"), "Url"), new CodeVariableReferenceExpression("url")));
  184. ifStmt.TrueStatements.Add(ifUrlNotEmpty);
  185. }
  186. // object obj holder for args dictionary lookup
  187. ifStmt.TrueStatements.Add(new CodeVariableDeclarationStatement(typeof(object), "obj"));
  188. // <method>(ConvertValue(arg1, argType1...), ConvertValue(arg2, argType1...), ...)
  189. CodeMethodInvokeExpression methodCallExpr = new CodeMethodInvokeExpression();
  190. methodCallExpr.Method = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("proxy"), methodInfo.ServerName);
  191. for (int i = 0; i < paramData.Length; ++i) {
  192. Type argType = paramData[i].ParameterType;
  193. string argName = "arg" + i;
  194. // ArgTypeN argN
  195. ifStmt.TrueStatements.Add(new CodeVariableDeclarationStatement(argType, argName));
  196. // if (!Dict<name>.TryGet(argName, argn)) throw new ArgumentException()
  197. CodeConditionStatement findArgStmt = new CodeConditionStatement();
  198. findArgStmt.Condition = new CodeMethodInvokeExpression(new CodeArgumentReferenceExpression("args"),
  199. "TryGetValue",
  200. new CodePrimitiveExpression(paramData[i].Name),
  201. new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression("obj")));
  202. findArgStmt.FalseStatements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(ArgumentException),
  203. new CodePrimitiveExpression("Argument not found: " + paramData[i].Name))));
  204. ifStmt.TrueStatements.Add(findArgStmt);
  205. // argn = (argtype)ConvertToType(obj, argtype, "paramname");
  206. ifStmt.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression(argName),
  207. new CodeCastExpression(argType, new CodeMethodInvokeExpression(new CodeSnippetExpression("BridgeHandler"),
  208. "ConvertToType",
  209. new CodeVariableReferenceExpression("obj"),
  210. new CodeTypeOfExpression(argType)))));
  211. // Build up the arg1, arg2, argn for the method call;
  212. methodCallExpr.Parameters.Add(new CodeVariableReferenceExpression(argName));
  213. }
  214. ifStmt.TrueStatements.Add(new CodeMethodReturnStatement(methodCallExpr));
  215. method.Statements.Add(ifStmt);
  216. }
  217. method.Statements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(ArgumentException),
  218. new CodePrimitiveExpression("CallServiceClassMethod: Unknown method"))));
  219. return method;
  220. }
  221. /*
  222. * [WebMethod]
  223. * [ScriptMethod(UseHttpGet=true,ResponseFormat=ResponseFormat.Json|Xml)]
  224. * [XmlInclude(typeof<>) ...
  225. * public object Method(IDictionary args) {
  226. * return Invoke(new BridgeRequest("Method", args);
  227. * }
  228. *
  229. * __invokeBridge takes the method as an argument
  230. */
  231. private static CodeMemberMethod GenerateWebMethodCode(String methodName, List<string> xmlIncludes, bool getEnabled, ResponseFormat responseFormat, bool invokeBridge) {
  232. CodeMemberMethod method = new CodeMemberMethod();
  233. method.Attributes = MemberAttributes.Public;
  234. method.Name = methodName;
  235. method.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(WebMethodAttribute))));
  236. string format = "Json";
  237. if (responseFormat != ResponseFormat.Json) {
  238. format = "Xml";
  239. }
  240. CodeAttributeDeclaration webOp = new CodeAttributeDeclaration(new CodeTypeReference(typeof(ScriptMethodAttribute)),
  241. new CodeAttributeArgument("UseHttpGet", new CodePrimitiveExpression(getEnabled)),
  242. new CodeAttributeArgument("ResponseFormat", new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(ResponseFormat)), format)));
  243. method.CustomAttributes.Add(webOp);
  244. if (invokeBridge) {
  245. method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "method"));
  246. }
  247. else {
  248. for (int i = 0; i < xmlIncludes.Count; ++i) {
  249. method.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(GenerateScriptTypeAttribute)),
  250. new CodeAttributeArgument(new CodeTypeOfExpression(xmlIncludes[i]))));
  251. }
  252. }
  253. method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IDictionary), "args"));
  254. method.ReturnType = new CodeTypeReference(typeof(object));
  255. CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression();
  256. expr.Method = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "Invoke");
  257. CodeExpression methodExpr = null;
  258. if (invokeBridge) {
  259. methodExpr = new CodeVariableReferenceExpression("method");
  260. }
  261. else {
  262. methodExpr = new CodePrimitiveExpression(methodName);
  263. }
  264. expr.Parameters.Add(new CodeObjectCreateExpression(typeof(BridgeRequest), methodExpr, new CodeVariableReferenceExpression("args")));
  265. method.Statements.Add(new CodeMethodReturnStatement(expr));
  266. return method;
  267. }
  268. public override Type GetGeneratedType(CompilerResults results) {
  269. return results.CompiledAssembly.GetType(Service.Namespace + "." + Service.Classname);
  270. }
  271. }
  272. }