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