2007-11-01 Atsushi Enomotot <atsushi@ximian.com>
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Discovery / DiscoveryClientProtocol.cs
1 // 
2 // System.Web.Services.Protocols.DiscoveryClientProtocol.cs
3 //
4 // Author:
5 //   Dave Bettin (javabettin@yahoo.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Dave Bettin, 2002
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections;
33 using System.IO;
34 using System.Web.Services.Protocols;
35 using System.Web.Services.Description;
36 using System.Xml;
37 using System.Xml.Schema;
38 using System.Xml.Serialization;
39 using System.Net;
40 using System.Text.RegularExpressions;
41
42 namespace System.Web.Services.Discovery {
43         public class DiscoveryClientProtocol : HttpWebClientProtocol {
44
45                 #region Fields
46
47                 private IList additionalInformation = new ArrayList ();
48                 private DiscoveryClientDocumentCollection documents = new DiscoveryClientDocumentCollection();
49                 private DiscoveryExceptionDictionary errors = new DiscoveryExceptionDictionary();
50                 private DiscoveryClientReferenceCollection references = new DiscoveryClientReferenceCollection();
51
52                 #endregion // Fields
53
54                 #region Constructors
55
56                 public DiscoveryClientProtocol () 
57                 {
58                 }
59                 
60                 #endregion // Constructors
61
62                 #region Properties
63
64                 public IList AdditionalInformation {
65                         get { return additionalInformation; }
66                 }
67                 
68                 public DiscoveryClientDocumentCollection Documents {
69                         get { return documents; }
70                 }
71                 
72                 public DiscoveryExceptionDictionary Errors {
73                         get { return errors; }
74                 }
75
76                 public DiscoveryClientReferenceCollection References {
77                         get { return references; }
78                 }               
79                 
80                 #endregion // Properties
81
82                 #region Methods
83
84                 public DiscoveryDocument Discover (string url)
85                 {
86                         Stream stream = Download (ref url);
87                         XmlTextReader reader = new XmlTextReader (url, stream);
88                         reader.XmlResolver = null;
89                         if (!DiscoveryDocument.CanRead (reader)) 
90                                 throw new InvalidOperationException ("The url '" + url + "' does not point to a valid discovery document");
91                                 
92                         DiscoveryDocument doc = DiscoveryDocument.Read (reader);
93                         reader.Close ();
94                         documents.Add (url, doc);
95                         AddDiscoReferences (doc);
96                         return doc;
97                 }
98
99                 public DiscoveryDocument DiscoverAny (string url)
100                 {
101                         try
102                         {
103                                 string contentType = null;
104                                 Stream stream = Download (ref url, ref contentType);
105         
106                                 if (contentType.IndexOf ("text/html") != -1)
107                                 {
108                                         // Look for an alternate url
109                                         
110                                         StreamReader sr = new StreamReader (stream);
111                                         string str = sr.ReadToEnd ();
112                                         
113                                         string rex = "link\\s*rel\\s*=\\s*[\"']?alternate[\"']?\\s*";
114                                         rex += "type\\s*=\\s*[\"']?text/xml[\"']?\\s*href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|'(?<1>[^']*)'|(?<1>\\S+))";
115                                         Regex rob = new Regex (rex, RegexOptions.IgnoreCase);
116                                         Match m = rob.Match (str);
117                                         if (!m.Success) 
118                                                 throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");
119                                         
120                                         if (url.StartsWith ("/"))
121                                         {
122                                                 Uri uri = new Uri (url);
123                                                 url = uri.GetLeftPart (UriPartial.Authority) + m.Groups[1];
124                                         }
125                                         else
126                                         {
127                                                 int i = url.LastIndexOf ('/');
128                                                 if (i == -1)
129                                                         throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");
130
131                                                 Uri tmp = new Uri (url);
132                                                 tmp = new Uri (tmp, m.Groups [1].ToString ());
133                                                 url = tmp.ToString ();
134                                         }
135                                         stream = Download (ref url);
136                                 }
137                                 
138                                 XmlTextReader reader = new XmlTextReader (url, stream);
139                                 reader.XmlResolver = null;
140                                 reader.MoveToContent ();
141                                 DiscoveryDocument doc;
142                                 DiscoveryReference refe = null;
143                                 
144                                 if (DiscoveryDocument.CanRead (reader))
145                                 {
146                                         doc = DiscoveryDocument.Read (reader);
147                                         documents.Add (url, doc);
148                                         refe = new DiscoveryDocumentReference ();
149                                         AddDiscoReferences (doc);
150                                 }
151                                 else if (ServiceDescription.CanRead (reader))
152                                 {
153                                         ServiceDescription wsdl = ServiceDescription.Read (reader);
154                                         documents.Add (url, wsdl);
155                                         doc = new DiscoveryDocument ();
156                                         refe = new ContractReference ();
157                                         doc.References.Add (refe);
158                                         refe.Url = url;
159                                         ((ContractReference)refe).ResolveInternal (this, wsdl);
160                                 }
161                                 else
162                                 {
163                                         XmlSchema schema = XmlSchema.Read (reader, null);
164                                         documents.Add (url, schema);
165                                         doc = new DiscoveryDocument ();
166                                         refe = new SchemaReference ();
167                                         refe.Url = url;
168                                         ((SchemaReference)refe).ResolveInternal (this, schema);
169                                         doc.References.Add (refe);
170                                 }
171                                 
172                                 refe.ClientProtocol = this;
173                                 refe.Url = url;
174                                 references.Add (url, refe);
175                                         
176                                 reader.Close ();
177                                 return doc;
178                         }
179                         catch (DiscoveryException ex) {
180                                 throw ex.Exception;
181                         }
182                 }
183                 
184                 void AddDiscoReferences (DiscoveryDocument doc)
185                 {
186                         foreach (DiscoveryReference re in doc.References)
187                         {
188                                 re.ClientProtocol = this;
189                                 references.Add (re.Url, re);
190                         }
191                         
192                         if (doc.AdditionalInfo != null) {
193                                 foreach (object info in doc.AdditionalInfo)
194                                         additionalInformation.Add (info);
195                         }
196                 }
197                 
198                 public Stream Download (ref string url)
199                 {
200                         string contentType = null;
201                         return Download (ref url, ref contentType);
202                 }
203                 
204                 public Stream Download (ref string url, ref string contentType)
205                 {
206                         if (url.StartsWith ("http://") || url.StartsWith ("https://"))
207                         {
208                                 WebRequest request = GetWebRequest (new Uri(url));
209                                 WebResponse resp = request.GetResponse ();
210                                 contentType = resp.ContentType;
211                                 return resp.GetResponseStream ();
212                         }
213                         else if (url.StartsWith ("file://"))
214                         {
215                                 WebRequest request = WebRequest.Create (new Uri (url));
216                                 WebResponse resp = request.GetResponse ();
217                                 contentType = resp.ContentType;
218                                 return resp.GetResponseStream ();
219                         }
220                         else
221                         {
222                                 string ext = Path.GetExtension (url).ToLower();
223                                 if (ext == ".wsdl" || ext == ".xsd")
224                                 {
225                                         contentType = "text/xml";
226                                         return new FileStream (url, FileMode.Open, FileAccess.Read);
227                                 }
228                                 else
229                                         throw new InvalidOperationException ("Unrecognized file type '" + url + "'. Extension must be one of .wsdl or .xsd");
230                         }
231                 }
232
233 #if NET_2_0
234                 [System.Runtime.InteropServices.ComVisible (false)]
235 #endif
236                 [Obsolete ("This method will be removed from a future version. The method call is no longer required for resource discovery", false)]
237                 public void LoadExternals ()
238                 {
239                 }
240
241                 public DiscoveryClientResultCollection ReadAll (string topLevelFilename)
242                 {
243                         StreamReader sr = new StreamReader (topLevelFilename);
244                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));
245                         DiscoveryClientResultsFile resfile = (DiscoveryClientResultsFile) ser.Deserialize (sr);
246                         sr.Close ();
247                         
248                         string basePath = Path.GetDirectoryName (topLevelFilename);
249                         
250                         foreach (DiscoveryClientResult dcr in resfile.Results)
251                         {
252                                 Type type = Type.GetType (dcr.ReferenceTypeName);
253                                 DiscoveryReference dr = (DiscoveryReference) Activator.CreateInstance (type);
254                                 dr.Url = dcr.Url;
255                                 FileStream fs = new FileStream (Path.Combine (basePath, dcr.Filename), FileMode.Open, FileAccess.Read);
256                                 Documents.Add (dr.Url, dr.ReadDocument (fs));
257                                 fs.Close ();
258                                 References.Add (dr.Url, dr);
259                         }
260                         return resfile.Results;
261                 }
262
263                 public void ResolveAll ()
264                 {
265                         ArrayList list = new ArrayList (References.Values);
266                         foreach (DiscoveryReference re in list)
267                         {
268                                 try
269                                 {
270                                         if (re is DiscoveryDocumentReference)
271                                                 ((DiscoveryDocumentReference)re).ResolveAll ();
272                                         else
273                                                 re.Resolve ();
274                                 }
275                                 catch (DiscoveryException ex)
276                                 {
277                                         Errors [ex.Url] = ex.Exception; 
278                                 }
279                                 catch (Exception ex)
280                                 {
281                                         Errors [re.Url] = ex;   
282                                 }
283                         }
284                 }
285                 
286                 public void ResolveOneLevel ()
287                 {
288                         ArrayList list = new ArrayList (References.Values);
289                         foreach (DiscoveryReference re in list)
290                                 re.Resolve ();
291                 }
292                 
293                 public DiscoveryClientResultCollection WriteAll (string directory, string topLevelFilename)
294                 {
295                         DiscoveryClientResultsFile resfile = new DiscoveryClientResultsFile();
296                         
297                         foreach (DiscoveryReference re in References.Values)
298                         {
299                                 object doc = Documents [re.Url];
300                                 if (doc == null) continue;
301                                 
302                                 string fileName = FindValidName (resfile, re.DefaultFilename);
303                                 resfile.Results.Add (new DiscoveryClientResult (re.GetType(), re.Url, fileName));
304                                 
305                                 string filepath = Path.Combine (directory, fileName);
306                                 FileStream fs = new FileStream (filepath, FileMode.Create, FileAccess.Write);
307                                 re.WriteDocument (doc, fs);
308                                 fs.Close ();
309                         }
310                         
311                         StreamWriter sw = new StreamWriter (Path.Combine (directory, topLevelFilename));
312                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));
313                         ser.Serialize (sw, resfile);
314                         sw.Close ();
315                         return resfile.Results;
316                 }
317                 
318                 string FindValidName (DiscoveryClientResultsFile resfile, string baseName)
319                 {
320                         string name = baseName;
321                         int id = 0;
322                         bool found;
323                         do
324                         {
325                                 found = false;
326                                 foreach (DiscoveryClientResult res in resfile.Results)
327                                 {
328                                         if (name == res.Filename) {
329                                                 found = true; break;
330                                         }
331                                 }
332                                 if (found)
333                                         name = Path.GetFileNameWithoutExtension (baseName) + (++id) + Path.GetExtension (baseName);
334                         }
335                         while (found);
336                         
337                         return name;
338                 }
339                 
340                 #endregion // Methods
341                 
342                 #region Classes
343                 
344                 public sealed class DiscoveryClientResultsFile {
345                         
346                         #region Fields
347                         
348                         private DiscoveryClientResultCollection results;
349
350                         #endregion // Fields
351
352                         #region Contructors
353                         
354                         public DiscoveryClientResultsFile () 
355                         {
356                                 results = new DiscoveryClientResultCollection ();
357                         }
358                 
359                         #endregion // Constructors
360                         
361                         #region Properties
362                 
363                         public DiscoveryClientResultCollection Results {                                
364                                 get { return results; }
365                         }
366                         
367                         #endregion // Properties
368                 }
369                 
370                 #endregion // Classes
371         }
372                 
373         internal class DiscoveryException : Exception
374         {
375                 public string Url;
376                 public Exception Exception;
377                 
378                 public DiscoveryException (string url, Exception origin)
379                 {
380                         Url = url;
381                         Exception = origin;
382                 }
383         }
384 }