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
14 using System.Xml.Schema;
\r
15 using System.Collections;
\r
16 using System.CodeDom;
\r
17 using System.CodeDom.Compiler;
\r
20 using System.Web.Services.Description;
\r
21 using System.Web.Services.Discovery;
\r
22 using System.Reflection;
\r
24 using Microsoft.CSharp;
\r
26 namespace Mono.WebServices
\r
30 /// Source code generator.
\r
33 class SourceGenerator
\r
35 string applicationSiganture = null;
\r
36 string appSettingURLKey = null;
\r
37 string appSettingBaseURL = null;
\r
38 string language = "CS";
\r
40 string outFilename = null;
\r
41 string protocol = "Soap";
\r
42 bool server = false;
\r
47 public string Language
\r
50 set { language = value; }
\r
56 public string Namespace
\r
63 /// The file to contain the generated code.
\r
66 public string Filename
\r
68 set { outFilename = value; }
\r
74 public string Protocol
\r
77 set { protocol = value; }
\r
78 get { return protocol; }
\r
84 public string ApplicationSignature
\r
86 set { applicationSiganture = value; }
\r
92 public string AppSettingURLKey
\r
94 set { appSettingURLKey = value; }
\r
100 public string AppSettingBaseURL
\r
102 set { appSettingBaseURL = value; }
\r
110 set { server = value; }
\r
115 /// Generate code for the specified ServiceDescription.
\r
118 public bool GenerateCode (ArrayList descriptions, ArrayList schemas)
\r
120 // FIXME iterate over each serviceDescription.Services?
\r
121 CodeNamespace codeNamespace = GetCodeNamespace();
\r
122 CodeCompileUnit codeUnit = new CodeCompileUnit();
\r
123 bool hasWarnings = false;
\r
125 codeUnit.Namespaces.Add(codeNamespace);
\r
127 ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
\r
128 importer.ProtocolName = protocol;
\r
130 importer.Style = ServiceDescriptionImportStyle.Server;
\r
132 foreach (ServiceDescription sd in descriptions)
\r
133 importer.AddServiceDescription(sd, appSettingURLKey, appSettingBaseURL);
\r
135 foreach (XmlSchema sc in schemas)
\r
136 importer.Schemas.Add (sc);
\r
138 ServiceDescriptionImportWarnings warnings = importer.Import(codeNamespace, codeUnit);
\r
141 if ((warnings & ServiceDescriptionImportWarnings.NoCodeGenerated) > 0)
\r
142 Console.WriteLine ("WARNING: No proxy class was generated");
\r
143 if ((warnings & ServiceDescriptionImportWarnings.NoMethodsGenerated) > 0)
\r
144 Console.WriteLine ("WARNING: The proxy class generated includes no methods");
\r
145 if ((warnings & ServiceDescriptionImportWarnings.OptionalExtensionsIgnored) > 0)
\r
146 Console.WriteLine ("WARNING: At least one optional extension has been ignored");
\r
147 if ((warnings & ServiceDescriptionImportWarnings.RequiredExtensionsIgnored) > 0)
\r
148 Console.WriteLine ("WARNING: At least one necessary extension has been ignored");
\r
149 if ((warnings & ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored) > 0)
\r
150 Console.WriteLine ("WARNING: At least one binding is of an unsupported type and has been ignored");
\r
151 if ((warnings & ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored) > 0)
\r
152 Console.WriteLine ("WARNING: At least one operation is of an unsupported type and has been ignored");
\r
153 hasWarnings = true;
\r
156 string fileName = null;
\r
157 bool hasBindings = false;
\r
159 foreach (ServiceDescription desc in descriptions)
\r
161 if (fileName == null && desc.Services.Count > 0)
\r
162 fileName = desc.Services[0].Name;
\r
164 if (desc.Bindings.Count > 0 || desc.Services.Count > 0)
\r
165 hasBindings = true;
\r
168 if (fileName == null)
\r
169 fileName = "output";
\r
172 WriteCodeUnit (codeUnit, fileName);
\r
174 return hasWarnings;
\r
179 /// Create the CodeNamespace with the generator's signature commented in.
\r
182 CodeNamespace GetCodeNamespace()
\r
184 CodeNamespace codeNamespace = new CodeNamespace(ns);
\r
186 if (applicationSiganture != null)
\r
188 codeNamespace.Comments.Add(new CodeCommentStatement("\n This source code was auto-generated by " + applicationSiganture + "\n"));
\r
191 return codeNamespace;
\r
197 void WriteCodeUnit(CodeCompileUnit codeUnit, string serviceName)
\r
199 CodeDomProvider provider = GetProvider();
\r
200 ICodeGenerator generator = provider.CreateGenerator();
\r
201 CodeGeneratorOptions options = new CodeGeneratorOptions();
\r
204 if (outFilename != null)
\r
205 filename = outFilename;
\r
207 filename = serviceName + "." + provider.FileExtension;
\r
209 Console.WriteLine ("Writing file '{0}'", filename);
\r
210 StreamWriter writer = new StreamWriter(filename);
\r
211 generator.GenerateCodeFromCompileUnit(codeUnit, writer, options);
\r
217 /// Fetch the Code Provider for the language specified by the 'language' members.
\r
220 private CodeDomProvider GetProvider()
\r
222 CodeDomProvider provider;
\r
225 switch (language.ToUpper ()) {
\r
227 provider = new CSharpCodeProvider ();
\r
230 provider = new Microsoft.VisualBasic.VBCodeProvider ();
\r
233 type = Type.GetType("Boo.Lang.CodeDom.BooCodeProvider, Boo.Lang.CodeDom, Version=1.0.0.0, Culture=neutral, PublicKeyToken=32c39770e9a21a67");
\r
235 return (CodeDomProvider) Activator.CreateInstance (type);
\r
237 throw new Exception ("Boo.Lang.CodeDom.BooCodeProvider not available");
\r
240 type = Type.GetType(language);
\r
241 if (type != null) {
\r
242 return (CodeDomProvider) Activator.CreateInstance (type);
\r
244 throw new Exception ("Unknown language");
\r
252 /// monoWSDL's main application driver. Reads the command-line arguments and dispatch the
\r
253 /// appropriate handlers.
\r
256 public class Driver
\r
258 const string ProductId = "Mono Web Services Description Language Utility";
\r
259 const string UsageMessage =
\r
260 "wsdl [options] {path | URL} \n\n"
\r
261 + " -d, -domain:domain Domain of username for server authentication.\n"
\r
262 + " -l, -language:language Language of generated code. Allowed CS (default)\n"
\r
263 + " and VB. You can also specify the fully qualified\n"
\r
264 + " name of a class that implements the\n"
\r
265 + " System.CodeDom.Compiler.CodeDomProvider Class.\n"
\r
266 + " -n, -namespace:ns The namespace of the generated code, default\n"
\r
267 + " namespace if none.\n"
\r
268 + " -nologo Surpress the startup logo.\n"
\r
269 + " -o, -out:filename The target file for generated code.\n"
\r
270 + " -p, -password:pwd Password used to contact the server.\n"
\r
271 + " -protocol:protocol Protocol to implement. Allowed: Soap (default),\n"
\r
272 + " HttpGet or HttpPost.\n"
\r
273 + " -server Generate server instead of client proxy code.\n"
\r
274 + " -u, -username:username Username used to contact the server.\n"
\r
275 + " -proxy:url Address of the proxy.\n"
\r
276 + " -pu, -proxyusername:username Username used to contact the proxy.\n"
\r
277 + " -pp, -proxypassword:pwd Password used to contact the proxy.\n"
\r
278 + " -pd, -proxydomain:domain Domain of username for proxy authentication.\n"
\r
279 + " -urlkey, -appsettingurlkey:key Configuration key that contains the default\n"
\r
280 + " url for the generated WS proxy.\n"
\r
281 + " -baseurl, -appsettingbaseurl:url Base url to use when constructing the\n"
\r
282 + " service url.\n"
\r
283 + " -type:typename,assembly Generate a proxy for a compiled web service\n"
\r
284 + " class. The URL parameter can be used to provide\n"
\r
285 + " the location of the service.\n"
\r
286 + " -sample:[binding/]operation Display a sample SOAP request and response.\n"
\r
287 + " -? Display this message\n"
\r
289 + "Options can be of the forms -option, --option or /option\n";
\r
291 SourceGenerator generator = null;
\r
293 ArrayList descriptions = new ArrayList ();
\r
294 ArrayList schemas = new ArrayList ();
\r
296 bool noLogo = false;
\r
298 bool hasURL = false;
\r
299 string sampleSoap = null;
\r
301 string proxyAddress = null;
\r
302 string proxyDomain = null;
\r
303 string proxyPassword = null;
\r
304 string proxyUsername = null;
\r
314 /// Initialize the document retrieval component and the source code generator.
\r
319 generator = new SourceGenerator();
\r
320 generator.ApplicationSignature = ProductId;
\r
325 /// Interperet the command-line arguments and configure the relavent components.
\r
328 void ImportArgument(string argument)
\r
330 string optionValuePair;
\r
332 if (argument.StartsWith("--"))
\r
334 optionValuePair = argument.Substring(2);
\r
336 else if (argument.StartsWith("/") || argument.StartsWith("-"))
\r
338 optionValuePair = argument.Substring(1);
\r
350 int indexOfEquals = optionValuePair.IndexOf(':');
\r
351 if (indexOfEquals > 0)
\r
353 option = optionValuePair.Substring(0, indexOfEquals);
\r
354 value = optionValuePair.Substring(indexOfEquals + 1);
\r
358 option = optionValuePair;
\r
364 case "appsettingurlkey":
\r
366 generator.AppSettingURLKey = value;
\r
369 case "appsettingbaseurl":
\r
371 generator.AppSettingBaseURL = value;
\r
381 generator.Language = value;
\r
386 generator.Namespace = value;
\r
395 generator.Filename = value;
\r
404 generator.Protocol = value;
\r
408 proxyAddress = value;
\r
411 case "proxydomain":
\r
413 proxyDomain = value;
\r
416 case "proxypassword":
\r
418 proxyPassword = value;
\r
421 case "proxyusername":
\r
423 proxyUsername = value;
\r
427 generator.Server = true;
\r
436 sampleSoap = value;
\r
449 if (argument.StartsWith ("/") && argument.IndexOfAny (Path.InvalidPathChars) == -1) {
\r
455 throw new Exception("Unknown option '" + option + "'");
\r
459 DiscoveryClientProtocol CreateClient ()
\r
461 DiscoveryClientProtocol dcc = new DiscoveryClientProtocol ();
\r
463 if (username != null || password != null || domain != null)
\r
465 NetworkCredential credentials = new NetworkCredential();
\r
467 if (username != null)
\r
468 credentials.UserName = username;
\r
470 if (password != null)
\r
471 credentials.Password = password;
\r
473 if (domain != null)
\r
474 credentials.Domain = domain;
\r
476 dcc.Credentials = credentials;
\r
479 if (proxyAddress != null)
\r
481 WebProxy proxy = new WebProxy (proxyAddress);
\r
482 if (proxyUsername != null || proxyPassword != null || proxyDomain != null)
\r
484 NetworkCredential credentials = new NetworkCredential();
\r
486 if (proxyUsername != null)
\r
487 credentials.UserName = proxyUsername;
\r
489 if (proxyPassword != null)
\r
490 credentials.Password = proxyPassword;
\r
492 if (proxyDomain != null)
\r
493 credentials.Domain = proxyDomain;
\r
495 proxy.Credentials = credentials;
\r
504 /// Driver's main control flow:
\r
505 /// - parse arguments
\r
506 /// - report required messages
\r
507 /// - terminate if no input
\r
508 /// - report errors
\r
511 int Run(string[] args)
\r
515 // parse command line arguments
\r
516 foreach (string argument in args)
\r
518 ImportArgument(argument);
\r
521 if (noLogo == false)
\r
522 Console.WriteLine(ProductId);
\r
524 if (help || (!hasURL && className == null))
\r
526 Console.WriteLine(UsageMessage);
\r
530 if (className == null)
\r
532 DiscoveryClientProtocol dcc = CreateClient ();
\r
533 dcc.AllowAutoRedirect = true;
\r
535 if (!url.StartsWith ("http://") && !url.StartsWith ("https://") && !url.StartsWith ("file://"))
\r
536 url = new Uri (Path.GetFullPath (url)).ToString ();
\r
538 dcc.DiscoverAny (url);
\r
541 foreach (object doc in dcc.Documents.Values)
\r
543 if (doc is ServiceDescription)
\r
544 descriptions.Add ((ServiceDescription)doc);
\r
545 else if (doc is XmlSchema)
\r
546 schemas.Add ((XmlSchema)doc);
\r
549 if (descriptions.Count == 0)
\r
550 throw new Exception ("No WSDL document was found at the url " + url);
\r
554 string[] names = className.Split (',');
\r
555 if (names.Length != 2) throw new Exception ("Invalid parameter value for 'type'");
\r
556 string cls = names[0].Trim ();
\r
557 string assembly = names[1].Trim ();
\r
559 Assembly asm = Assembly.LoadFrom (assembly);
\r
560 Type t = asm.GetType (cls);
\r
561 if (t == null) throw new Exception ("Type '" + cls + "' not found in assembly " + assembly);
\r
562 ServiceDescriptionReflector reflector = new ServiceDescriptionReflector ();
\r
563 reflector.Reflect (t, url);
\r
564 foreach (XmlSchema s in reflector.Schemas)
\r
567 foreach (ServiceDescription sd in reflector.ServiceDescriptions)
\r
568 descriptions.Add (sd);
\r
571 if (sampleSoap != null)
\r
573 ConsoleSampleGenerator.Generate (descriptions, schemas, sampleSoap, generator.Protocol);
\r
577 // generate the code
\r
578 if (generator.GenerateCode (descriptions, schemas))
\r
583 catch (NullReferenceException e)
\r
585 Console.WriteLine (e);
\r
588 catch (Exception exception)
\r
590 Console.WriteLine("Error: {0}", exception.Message);
\r
591 // FIXME: surpress this except for when debug is enabled
\r
592 //Console.WriteLine("Stack:\n {0}", exception.StackTrace);
\r
599 /// Application entry point.
\r
602 public static int Main(string[] args)
\r
604 Driver d = new Driver();
\r
605 return d.Run(args);
\r