* ContractReference.cs, DiscoveryClientProtocol.cs: Set the url from which
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Discovery / DiscoveryClientProtocol.cs
1 // \r
2 // System.Web.Services.Protocols.DiscoveryClientProtocol.cs\r
3 //\r
4 // Author:\r
5 //   Dave Bettin (javabettin@yahoo.com)\r
6 //   Lluis Sanchez Gual (lluis@ximian.com)\r
7 //\r
8 // Copyright (C) Dave Bettin, 2002\r
9 //\r
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 \r
32 using System.Collections;\r
33 using System.IO;\r
34 using System.Web.Services.Protocols;\r
35 using System.Web.Services.Description;\r
36 using System.Xml;\r
37 using System.Xml.Schema;\r
38 using System.Xml.Serialization;\r
39 using System.Net;\r
40 using System.Text.RegularExpressions;\r
41 \r
42 namespace System.Web.Services.Discovery {\r
43         public class DiscoveryClientProtocol : HttpWebClientProtocol {\r
44 \r
45                 #region Fields\r
46 \r
47                 private IList additionalInformation = new ArrayList ();\r
48                 private DiscoveryClientDocumentCollection documents = new DiscoveryClientDocumentCollection();\r
49                 private DiscoveryExceptionDictionary errors = new DiscoveryExceptionDictionary();\r
50                 private DiscoveryClientReferenceCollection references = new DiscoveryClientReferenceCollection();\r
51 \r
52                 #endregion // Fields\r
53 \r
54                 #region Constructors\r
55 \r
56                 public DiscoveryClientProtocol () \r
57                 {\r
58                 }\r
59                 \r
60                 #endregion // Constructors\r
61 \r
62                 #region Properties\r
63 \r
64                 public IList AdditionalInformation {\r
65                         get { return additionalInformation; }\r
66                 }\r
67                 \r
68                 public DiscoveryClientDocumentCollection Documents {\r
69                         get { return documents; }\r
70                 }\r
71                 \r
72                 public DiscoveryExceptionDictionary Errors {\r
73                         get { return errors; }\r
74                 }\r
75 \r
76                 public DiscoveryClientReferenceCollection References {\r
77                         get { return references; }\r
78                 }               \r
79                 \r
80                 #endregion // Properties\r
81 \r
82                 #region Methods\r
83 \r
84                 public DiscoveryDocument Discover (string url)\r
85                 {\r
86                         Stream stream = Download (ref url);\r
87                         XmlTextReader reader = new XmlTextReader (url, stream);\r
88                         if (!DiscoveryDocument.CanRead (reader)) \r
89                                 throw new InvalidOperationException ("The url '" + url + "' does not point to a valid discovery document");\r
90                                 \r
91                         DiscoveryDocument doc = DiscoveryDocument.Read (reader);\r
92                         reader.Close ();\r
93                         documents.Add (url, doc);\r
94                         AddDiscoReferences (doc);\r
95                         return doc;\r
96                 }\r
97 \r
98                 public DiscoveryDocument DiscoverAny (string url)\r
99                 {\r
100                         try\r
101                         {\r
102                                 string contentType = null;\r
103                                 Stream stream = Download (ref url, ref contentType);\r
104         \r
105                                 if (contentType.IndexOf ("text/html") != -1)\r
106                                 {\r
107                                         // Look for an alternate url\r
108                                         \r
109                                         StreamReader sr = new StreamReader (stream);\r
110                                         string str = sr.ReadToEnd ();\r
111                                         \r
112                                         string rex = "link\\s*rel\\s*=\\s*[\"']?alternate[\"']?\\s*";\r
113                                         rex += "type\\s*=\\s*[\"']?text/xml[\"']?\\s*href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|'(?<1>[^']*)'|(?<1>\\S+))";\r
114                                         Regex rob = new Regex (rex, RegexOptions.IgnoreCase);\r
115                                         Match m = rob.Match (str);\r
116                                         if (!m.Success) \r
117                                                 throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");\r
118                                         \r
119                                         if (url.StartsWith ("/"))\r
120                                         {\r
121                                                 Uri uri = new Uri (url);\r
122                                                 url = uri.GetLeftPart (UriPartial.Authority) + m.Groups[1];\r
123                                         }\r
124                                         else\r
125                                         {\r
126                                                 int i = url.LastIndexOf ('/');\r
127                                                 if (i == -1)\r
128                                                         throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");\r
129                                                 url = url.Substring (0,i+1) + m.Groups[1];\r
130                                         }\r
131                                         stream = Download (ref url);\r
132                                 }\r
133                                 \r
134                                 XmlTextReader reader = new XmlTextReader (url, stream);\r
135                                 reader.MoveToContent ();\r
136                                 DiscoveryDocument doc;\r
137                                 DiscoveryReference refe = null;\r
138                                 \r
139                                 if (DiscoveryDocument.CanRead (reader))\r
140                                 {\r
141                                         doc = DiscoveryDocument.Read (reader);\r
142                                         documents.Add (url, doc);\r
143                                         refe = new DiscoveryDocumentReference ();\r
144                                         AddDiscoReferences (doc);\r
145                                 }\r
146                                 else if (ServiceDescription.CanRead (reader))\r
147                                 {\r
148                                         ServiceDescription wsdl = ServiceDescription.Read (reader);\r
149                                         documents.Add (url, wsdl);\r
150                                         doc = new DiscoveryDocument ();\r
151                                         refe = new ContractReference ();\r
152                                         doc.References.Add (refe);\r
153                                         refe.Url = url;\r
154                                         ((ContractReference)refe).ResolveInternal (this, wsdl);\r
155                                 }\r
156                                 else\r
157                                 {\r
158                                         XmlSchema schema = XmlSchema.Read (reader, null);\r
159                                         documents.Add (url, schema);\r
160                                         doc = new DiscoveryDocument ();\r
161                                         refe = new SchemaReference ();\r
162                                         doc.References.Add (refe);\r
163                                 }\r
164                                 \r
165                                 refe.ClientProtocol = this;\r
166                                 refe.Url = url;\r
167                                 references.Add (url, refe);\r
168                                         \r
169                                 reader.Close ();\r
170                                 return doc;\r
171                         }\r
172                         catch (DiscoveryException ex) {\r
173                                 throw ex.Exception;\r
174                         }\r
175                 }\r
176                 \r
177                 void AddDiscoReferences (DiscoveryDocument doc)\r
178                 {\r
179                         foreach (DiscoveryReference re in doc.References)\r
180                         {\r
181                                 re.ClientProtocol = this;\r
182                                 references.Add (re.Url, re);\r
183                         }\r
184                         \r
185                         if (doc.AdditionalInfo != null) {\r
186                                 foreach (object info in doc.AdditionalInfo)\r
187                                         additionalInformation.Add (info);\r
188                         }\r
189                 }\r
190                 \r
191                 public Stream Download (ref string url)\r
192                 {\r
193                         string contentType = null;\r
194                         return Download (ref url, ref contentType);\r
195                 }\r
196                 \r
197                 public Stream Download (ref string url, ref string contentType)\r
198                 {\r
199                         if (url.StartsWith ("http://") || url.StartsWith ("https://"))\r
200                         {\r
201                                 WebRequest request = GetWebRequest (new Uri(url));\r
202                                 WebResponse resp = request.GetResponse ();\r
203                                 contentType = resp.ContentType;\r
204                                 return resp.GetResponseStream ();\r
205                         }\r
206                         else\r
207                         {\r
208                                 string ext = Path.GetExtension (url).ToLower();\r
209                                 if (ext == ".wsdl" || ext == ".xsd")\r
210                                 {\r
211                                         contentType = "text/xml";\r
212                                         return new FileStream (url, FileMode.Open, FileAccess.Read);\r
213                                 }\r
214                                 else\r
215                                         throw new InvalidOperationException ("Unrecognized file type '" + url + "'. Extension must be one of .wsdl or .xsd");\r
216                         }\r
217                 }\r
218                 \r
219                 public DiscoveryClientResultCollection ReadAll (string topLevelFilename)\r
220                 {\r
221                         StreamReader sr = new StreamReader (topLevelFilename);\r
222                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));\r
223                         DiscoveryClientResultsFile resfile = (DiscoveryClientResultsFile) ser.Deserialize (sr);\r
224                         sr.Close ();\r
225                         \r
226                         foreach (DiscoveryClientResult dcr in resfile.Results)\r
227                         {\r
228                                 Type type = Type.GetType (dcr.ReferenceTypeName);\r
229                                 DiscoveryReference dr = (DiscoveryReference) Activator.CreateInstance (type);\r
230                                 dr.Url = dcr.Url;\r
231                                 FileStream fs = new FileStream (dcr.Filename, FileMode.Open, FileAccess.Read);\r
232                                 Documents.Add (dr.Url, dr.ReadDocument (fs));\r
233                                 fs.Close ();\r
234                                 References.Add (dr.Url, dr);\r
235                         }\r
236                         return resfile.Results;\r
237                 }\r
238 \r
239                 public void ResolveAll ()\r
240                 {\r
241                         ArrayList list = new ArrayList (References.Values);\r
242                         foreach (DiscoveryReference re in list)\r
243                         {\r
244                                 try\r
245                                 {\r
246                                         if (re is DiscoveryDocumentReference)\r
247                                                 ((DiscoveryDocumentReference)re).ResolveAll ();\r
248                                         else\r
249                                                 re.Resolve ();\r
250                                 }\r
251                                 catch (DiscoveryException ex)\r
252                                 {\r
253                                         Errors [ex.Url] = ex.Exception; \r
254                                 }\r
255                                 catch (Exception ex)\r
256                                 {\r
257                                         Errors [re.Url] = ex;   \r
258                                 }\r
259                         }\r
260                 }\r
261                 \r
262                 public void ResolveOneLevel ()\r
263                 {\r
264                         ArrayList list = new ArrayList (References.Values);\r
265                         foreach (DiscoveryReference re in list)\r
266                                 re.Resolve ();\r
267                 }\r
268                 \r
269                 public DiscoveryClientResultCollection WriteAll (string directory, string topLevelFilename)\r
270                 {\r
271                         DiscoveryClientResultsFile resfile = new DiscoveryClientResultsFile();\r
272                         \r
273                         foreach (DiscoveryReference re in References.Values)\r
274                         {\r
275                                 object doc = Documents [re.Url];\r
276                                 if (doc == null) continue;\r
277                                 \r
278                                 string fileName = FindValidName (resfile, re.DefaultFilename);\r
279                                 resfile.Results.Add (new DiscoveryClientResult (re.GetType(), re.Url, fileName));\r
280                                 \r
281                                 string filepath = Path.Combine (directory, fileName);\r
282                                 FileStream fs = new FileStream (filepath, FileMode.Create, FileAccess.Write);\r
283                                 re.WriteDocument (doc, fs);\r
284                                 fs.Close ();\r
285                         }\r
286                         \r
287                         StreamWriter sw = new StreamWriter (Path.Combine (directory, topLevelFilename));\r
288                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));\r
289                         ser.Serialize (sw, resfile);\r
290                         sw.Close ();\r
291                         return resfile.Results;\r
292                 }\r
293                 \r
294                 string FindValidName (DiscoveryClientResultsFile resfile, string baseName)\r
295                 {\r
296                         string name = baseName;\r
297                         int id = 0;\r
298                         bool found;\r
299                         do\r
300                         {\r
301                                 found = false;\r
302                                 foreach (DiscoveryClientResult res in resfile.Results)\r
303                                 {\r
304                                         if (name == res.Filename) {\r
305                                                 found = true; break;\r
306                                         }\r
307                                 }\r
308                                 if (found)\r
309                                         name = Path.GetFileNameWithoutExtension (baseName) + (++id) + Path.GetExtension (baseName);\r
310                         }\r
311                         while (found);\r
312                         \r
313                         return name;\r
314                 }\r
315                 \r
316                 #endregion // Methods\r
317                 \r
318                 #region Classes\r
319                 \r
320                 public sealed class DiscoveryClientResultsFile {\r
321                         \r
322                         #region Fields\r
323                         \r
324                         private DiscoveryClientResultCollection results;\r
325 \r
326                         #endregion // Fields\r
327 \r
328                         #region Contructors\r
329                         \r
330                         public DiscoveryClientResultsFile () \r
331                         {\r
332                                 results = new DiscoveryClientResultCollection ();\r
333                         }\r
334                 \r
335                         #endregion // Constructors\r
336                         \r
337                         #region Properties\r
338                 \r
339                         public DiscoveryClientResultCollection Results {                                \r
340                                 get { return results; }\r
341                         }\r
342                         \r
343                         #endregion // Properties\r
344                 }\r
345                 \r
346                 #endregion // Classes\r
347         }\r
348                 \r
349         internal class DiscoveryException : Exception\r
350         {\r
351                 public string Url;\r
352                 public Exception Exception;\r
353                 \r
354                 public DiscoveryException (string url, Exception origin)\r
355                 {\r
356                         Url = url;\r
357                         Exception = origin;\r
358                 }\r
359         }\r
360 }