2 /// MonoWSDL.cs -- a WSDL to proxy code generator.
\r
4 /// Author: Erik LeBel (eriklebel@yahoo.ca)
\r
5 /// Lluis Sanchez (lluis@ximian.com)
\r
7 /// Copyright (C) 2003, Erik LeBel,
\r
13 using System.Collections.Specialized;
\r
15 using System.Xml.Schema;
\r
16 using System.Collections;
\r
17 using System.CodeDom;
\r
18 using System.CodeDom.Compiler;
\r
21 using System.Web.Services.Description;
\r
22 using System.Web.Services.Discovery;
\r
23 using System.Reflection;
\r
25 using Microsoft.CSharp;
\r
27 namespace Mono.WebServices
\r
31 /// Source code generator.
\r
34 class SourceGenerator
\r
36 string applicationSiganture = null;
\r
37 string appSettingURLKey = null;
\r
38 string appSettingBaseURL = null;
\r
39 string language = "CS";
\r
41 string outFilename = null;
\r
42 string protocol = "Soap";
\r
43 bool server = false;
\r
48 public string Language
\r
51 set { language = value; }
\r
57 public string Namespace
\r
64 /// The file to contain the generated code.
\r
67 public string Filename
\r
69 set { outFilename = value; }
\r
75 public string Protocol
\r
78 set { protocol = value; }
\r
79 get { return protocol; }
\r
85 public string ApplicationSignature
\r
87 set { applicationSiganture = value; }
\r
93 public string AppSettingURLKey
\r
95 set { appSettingURLKey = value; }
\r
101 public string AppSettingBaseURL
\r
103 set { appSettingBaseURL = value; }
\r
111 set { server = value; }
\r
116 /// Generate code for the specified ServiceDescription.
\r
119 public bool GenerateCode (ArrayList descriptions, ArrayList schemas)
\r
121 // FIXME iterate over each serviceDescription.Services?
\r
122 CodeNamespace codeNamespace = GetCodeNamespace();
\r
123 CodeCompileUnit codeUnit = new CodeCompileUnit();
\r
124 bool hasWarnings = false;
\r
126 codeUnit.Namespaces.Add(codeNamespace);
\r
128 ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
\r
129 importer.ProtocolName = protocol;
\r
131 importer.Style = ServiceDescriptionImportStyle.Server;
\r
133 foreach (ServiceDescription sd in descriptions)
\r
134 importer.AddServiceDescription(sd, appSettingURLKey, appSettingBaseURL);
\r
136 foreach (XmlSchema sc in schemas)
\r
137 importer.Schemas.Add (sc);
\r
139 ServiceDescriptionImportWarnings warnings = importer.Import(codeNamespace, codeUnit);
\r
142 if ((warnings & ServiceDescriptionImportWarnings.NoCodeGenerated) > 0)
\r
143 Console.WriteLine ("WARNING: No proxy class was generated");
\r
144 if ((warnings & ServiceDescriptionImportWarnings.NoMethodsGenerated) > 0)
\r
145 Console.WriteLine ("WARNING: The proxy class generated includes no methods");
\r
146 if ((warnings & ServiceDescriptionImportWarnings.OptionalExtensionsIgnored) > 0)
\r
147 Console.WriteLine ("WARNING: At least one optional extension has been ignored");
\r
148 if ((warnings & ServiceDescriptionImportWarnings.RequiredExtensionsIgnored) > 0)
\r
149 Console.WriteLine ("WARNING: At least one necessary extension has been ignored");
\r
150 if ((warnings & ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored) > 0)
\r
151 Console.WriteLine ("WARNING: At least one binding is of an unsupported type and has been ignored");
\r
152 if ((warnings & ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored) > 0)
\r
153 Console.WriteLine ("WARNING: At least one operation is of an unsupported type and has been ignored");
\r
154 hasWarnings = true;
\r
157 string fileName = null;
\r
158 bool hasBindings = false;
\r
160 foreach (ServiceDescription desc in descriptions)
\r
162 if (fileName == null && desc.Services.Count > 0)
\r
163 fileName = desc.Services[0].Name;
\r
165 if (desc.Bindings.Count > 0 || desc.Services.Count > 0)
\r
166 hasBindings = true;
\r
169 if (fileName == null)
\r
170 fileName = "output";
\r
173 WriteCodeUnit (codeUnit, fileName);
\r
175 return hasWarnings;
\r
180 /// Create the CodeNamespace with the generator's signature commented in.
\r
183 CodeNamespace GetCodeNamespace()
\r
185 CodeNamespace codeNamespace = new CodeNamespace(ns);
\r
187 if (applicationSiganture != null)
\r
189 codeNamespace.Comments.Add(new CodeCommentStatement("\n This source code was auto-generated by " + applicationSiganture + "\n"));
\r
192 return codeNamespace;
\r
198 void WriteCodeUnit(CodeCompileUnit codeUnit, string serviceName)
\r
200 CodeDomProvider provider = GetProvider();
\r
201 ICodeGenerator generator = provider.CreateGenerator();
\r
202 CodeGeneratorOptions options = new CodeGeneratorOptions();
\r
205 if (outFilename != null)
\r
206 filename = outFilename;
\r
208 filename = serviceName + "." + provider.FileExtension;
\r
210 Console.WriteLine ("Writing file '{0}'", filename);
\r
211 StreamWriter writer = new StreamWriter(filename);
\r
212 generator.GenerateCodeFromCompileUnit(codeUnit, writer, options);
\r
218 /// Fetch the Code Provider for the language specified by the 'language' members.
\r
221 private CodeDomProvider GetProvider()
\r
223 CodeDomProvider provider;
\r
226 switch (language.ToUpper ()) {
\r
228 provider = new CSharpCodeProvider ();
\r
231 provider = new Microsoft.VisualBasic.VBCodeProvider ();
\r
234 type = Type.GetType("Boo.Lang.CodeDom.BooCodeProvider, Boo.Lang.CodeDom, Version=1.0.0.0, Culture=neutral, PublicKeyToken=32c39770e9a21a67");
\r
236 return (CodeDomProvider) Activator.CreateInstance (type);
\r
238 throw new Exception ("Boo.Lang.CodeDom.BooCodeProvider not available");
\r
241 type = Type.GetType(language);
\r
242 if (type != null) {
\r
243 return (CodeDomProvider) Activator.CreateInstance (type);
\r
245 throw new Exception ("Unknown language");
\r
253 /// monoWSDL's main application driver. Reads the command-line arguments and dispatch the
\r
254 /// appropriate handlers.
\r
257 public class Driver
\r
259 const string ProductId = "Mono Web Services Description Language Utility";
\r
260 const string UsageMessage =
\r
261 "wsdl [options] {path | URL} \n\n"
\r
262 + " -d, -domain:domain Domain of username for server authentication.\n"
\r
263 + " -l, -language:language Language of generated code. Allowed CS (default)\n"
\r
264 + " and VB. You can also specify the fully qualified\n"
\r
265 + " name of a class that implements the\n"
\r
266 + " System.CodeDom.Compiler.CodeDomProvider Class.\n"
\r
267 + " -n, -namespace:ns The namespace of the generated code, default\n"
\r
268 + " namespace if none.\n"
\r
269 + " -nologo Surpress the startup logo.\n"
\r
270 + " -o, -out:filename The target file for generated code.\n"
\r
271 + " -p, -password:pwd Password used to contact the server.\n"
\r
272 + " -protocol:protocol Protocol to implement. Allowed: Soap (default),\n"
\r
273 + " HttpGet or HttpPost.\n"
\r
274 + " -server Generate server instead of client proxy code.\n"
\r
275 + " -u, -username:username Username used to contact the server.\n"
\r
276 + " -proxy:url Address of the proxy.\n"
\r
277 + " -pu, -proxyusername:username Username used to contact the proxy.\n"
\r
278 + " -pp, -proxypassword:pwd Password used to contact the proxy.\n"
\r
279 + " -pd, -proxydomain:domain Domain of username for proxy authentication.\n"
\r
280 + " -urlkey, -appsettingurlkey:key Configuration key that contains the default\n"
\r
281 + " url for the generated WS proxy.\n"
\r
282 + " -baseurl, -appsettingbaseurl:url Base url to use when constructing the\n"
\r
283 + " service url.\n"
\r
284 + " -type:typename,assembly Generate a proxy for a compiled web service\n"
\r
285 + " class. The URL parameter can be used to provide\n"
\r
286 + " the location of the service.\n"
\r
287 + " -sample:[binding/]operation Display a sample SOAP request and response.\n"
\r
288 + " -? Display this message\n"
\r
290 + "Options can be of the forms -option, --option or /option\n";
\r
292 SourceGenerator generator = null;
\r
294 ArrayList descriptions = new ArrayList ();
\r
295 ArrayList schemas = new ArrayList ();
\r
297 bool noLogo = false;
\r
299 bool hasURL = false;
\r
300 string sampleSoap = null;
\r
302 string proxyAddress = null;
\r
303 string proxyDomain = null;
\r
304 string proxyPassword = null;
\r
305 string proxyUsername = null;
\r
310 StringCollection urls = new StringCollection ();
\r
315 /// Initialize the document retrieval component and the source code generator.
\r
320 generator = new SourceGenerator();
\r
321 generator.ApplicationSignature = ProductId;
\r
326 /// Interperet the command-line arguments and configure the relavent components.
\r
329 void ImportArgument(string argument)
\r
331 string optionValuePair;
\r
333 if (argument.StartsWith("--"))
\r
335 optionValuePair = argument.Substring(2);
\r
337 else if (argument.StartsWith("/") || argument.StartsWith("-"))
\r
339 optionValuePair = argument.Substring(1);
\r
344 urls.Add (argument);
\r
351 int indexOfEquals = optionValuePair.IndexOf(':');
\r
352 if (indexOfEquals > 0)
\r
354 option = optionValuePair.Substring(0, indexOfEquals);
\r
355 value = optionValuePair.Substring(indexOfEquals + 1);
\r
359 option = optionValuePair;
\r
365 case "appsettingurlkey":
\r
367 generator.AppSettingURLKey = value;
\r
370 case "appsettingbaseurl":
\r
372 generator.AppSettingBaseURL = value;
\r
382 generator.Language = value;
\r
387 generator.Namespace = value;
\r
396 generator.Filename = value;
\r
405 generator.Protocol = value;
\r
409 proxyAddress = value;
\r
412 case "proxydomain":
\r
414 proxyDomain = value;
\r
417 case "proxypassword":
\r
419 proxyPassword = value;
\r
422 case "proxyusername":
\r
424 proxyUsername = value;
\r
428 generator.Server = true;
\r
437 sampleSoap = value;
\r
450 if (argument.StartsWith ("/") && argument.IndexOfAny (Path.InvalidPathChars) == -1) {
\r
452 urls.Add (argument);
\r
456 throw new Exception("Unknown option '" + option + "'");
\r
460 DiscoveryClientProtocol CreateClient ()
\r
462 DiscoveryClientProtocol dcc = new DiscoveryClientProtocol ();
\r
464 if (username != null || password != null || domain != null)
\r
466 NetworkCredential credentials = new NetworkCredential();
\r
468 if (username != null)
\r
469 credentials.UserName = username;
\r
471 if (password != null)
\r
472 credentials.Password = password;
\r
474 if (domain != null)
\r
475 credentials.Domain = domain;
\r
477 dcc.Credentials = credentials;
\r
480 if (proxyAddress != null)
\r
482 WebProxy proxy = new WebProxy (proxyAddress);
\r
483 if (proxyUsername != null || proxyPassword != null || proxyDomain != null)
\r
485 NetworkCredential credentials = new NetworkCredential();
\r
487 if (proxyUsername != null)
\r
488 credentials.UserName = proxyUsername;
\r
490 if (proxyPassword != null)
\r
491 credentials.Password = proxyPassword;
\r
493 if (proxyDomain != null)
\r
494 credentials.Domain = proxyDomain;
\r
496 proxy.Credentials = credentials;
\r
505 /// Driver's main control flow:
\r
506 /// - parse arguments
\r
507 /// - report required messages
\r
508 /// - terminate if no input
\r
509 /// - report errors
\r
512 int Run(string[] args)
\r
516 // parse command line arguments
\r
517 foreach (string argument in args)
\r
519 ImportArgument(argument);
\r
522 if (noLogo == false)
\r
523 Console.WriteLine(ProductId);
\r
525 if (help || (!hasURL && className == null))
\r
527 Console.WriteLine(UsageMessage);
\r
531 if (className == null)
\r
533 foreach (string urlEntry in urls) {
\r
534 string url = urlEntry;
\r
535 DiscoveryClientProtocol dcc = CreateClient ();
\r
536 dcc.AllowAutoRedirect = true;
\r
537 if (!url.StartsWith ("http://") && !url.StartsWith ("https://") && !url.StartsWith ("file://"))
\r
538 url = new Uri (Path.GetFullPath (url)).ToString ();
\r
540 dcc.DiscoverAny (url);
\r
543 foreach (object doc in dcc.Documents.Values)
\r
545 if (doc is ServiceDescription)
\r
546 descriptions.Add ((ServiceDescription)doc);
\r
547 else if (doc is XmlSchema)
\r
548 schemas.Add ((XmlSchema)doc);
\r
551 if (descriptions.Count == 0)
\r
552 throw new Exception ("No WSDL document was found at the url " + url);
\r
557 string[] names = className.Split (',');
\r
558 if (names.Length != 2) throw new Exception ("Invalid parameter value for 'type'");
\r
559 string cls = names[0].Trim ();
\r
560 string assembly = names[1].Trim ();
\r
562 Assembly asm = Assembly.LoadFrom (assembly);
\r
563 Type t = asm.GetType (cls);
\r
564 if (t == null) throw new Exception ("Type '" + cls + "' not found in assembly " + assembly);
\r
565 ServiceDescriptionReflector reflector = new ServiceDescriptionReflector ();
\r
566 foreach (string url in urls)
\r
567 reflector.Reflect (t, url);
\r
568 foreach (XmlSchema s in reflector.Schemas)
\r
571 foreach (ServiceDescription sd in reflector.ServiceDescriptions)
\r
572 descriptions.Add (sd);
\r
575 if (sampleSoap != null)
\r
577 ConsoleSampleGenerator.Generate (descriptions, schemas, sampleSoap, generator.Protocol);
\r
581 // generate the code
\r
582 generator.GenerateCode (descriptions, schemas);
\r
585 catch (NullReferenceException e)
\r
587 Console.WriteLine (e);
\r
590 catch (InvalidCastException e)
\r
592 Console.WriteLine (e);
\r
595 catch (Exception exception)
\r
597 Console.WriteLine("Error: {0}", exception.Message);
\r
598 // FIXME: surpress this except for when debug is enabled
\r
599 //Console.WriteLine("Stack:\n {0}", exception.StackTrace);
\r
606 /// Application entry point.
\r
609 public static int Main(string[] args)
\r
611 Driver d = new Driver();
\r
612 return d.Run(args);
\r