2006-11-02 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / TypeStubManager.cs
1 //
2 // Methods.cs: Information about a method and its mapping to a SOAP web service.
3 //
4 // Author:
5 //   Miguel de Icaza
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // (C) 2003 Ximian, Inc.
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.Reflection;
33 using System.Collections;
34 using System.Xml;
35 using System.Xml.Serialization;
36 using System.Web.Services;
37 using System.Web.Services.Description;
38
39 namespace System.Web.Services.Protocols {
40
41         //
42         // This class represents all the information we extract from a MethodInfo
43         // in the WebClientProtocol derivative stub class
44         //
45         internal class MethodStubInfo 
46         {
47                 internal LogicalMethodInfo MethodInfo;
48                 internal TypeStubInfo TypeStub;
49
50                 // The name used by the stub class to reference this method.
51                 internal string Name;
52                 internal WebMethodAttribute MethodAttribute;
53                 
54                 internal string OperationName
55                 {
56                         get { return MethodInfo.Name; }
57                 }
58
59                 //
60                 // Constructor
61                 //
62                 public MethodStubInfo (TypeStubInfo parent, LogicalMethodInfo source)
63                 {
64                         TypeStub = parent;
65                         MethodInfo = source;
66
67                         object [] o = source.GetCustomAttributes (typeof (WebMethodAttribute));
68                         if (o.Length > 0)
69                         {
70                                 MethodAttribute = (WebMethodAttribute) o [0];
71                                 Name = MethodAttribute.MessageName;
72                                 if (Name == "") Name = source.Name;
73                         }
74                         else
75                                 Name = source.Name;
76                 }
77         }
78
79         //
80         // Holds the metadata loaded from the type stub, as well as
81         // the metadata for all the methods in the type
82         //
83         internal class TypeStubInfo 
84         {
85                 Hashtable name_to_method = new Hashtable ();
86                 MethodStubInfo[] methods;
87                 ArrayList bindings = new ArrayList ();
88                 LogicalTypeInfo logicalType;
89                 string defaultBinding;
90                 ArrayList mappings;
91                 XmlSerializer[] serializers;
92
93                 public TypeStubInfo (LogicalTypeInfo logicalTypeInfo)
94                 {
95                         this.logicalType = logicalTypeInfo;
96
97                         defaultBinding = logicalType.WebServiceName + ProtocolName;
98                         BindingInfo binfo = new BindingInfo (defaultBinding, logicalType.WebServiceNamespace);
99                         Bindings.Add (binfo);
100                 }
101                 
102                 public LogicalTypeInfo LogicalType
103                 {
104                         get { return logicalType; }
105                 }
106                 
107                 public Type Type
108                 {
109                         get { return logicalType.Type; }
110                 }
111                 
112                 public string DefaultBinding
113                 {
114                         get { return defaultBinding; }
115                 }
116                 
117                 public virtual XmlReflectionImporter XmlImporter 
118                 {
119                         get { return null; }
120                 }
121
122                 public virtual SoapReflectionImporter SoapImporter 
123                 {
124                         get { return null; }
125                 }
126                 
127                 public virtual string ProtocolName
128                 {
129                         get { return null; }
130                 }
131                 
132                 public XmlSerializer GetSerializer (int n)
133                 {
134                         return serializers [n];
135                 }
136                 
137                 public int RegisterSerializer (XmlMapping map)
138                 {
139                         if (mappings == null) mappings = new ArrayList ();
140                         return mappings.Add (map);
141                 }
142
143                 public void Initialize ()
144                 {
145                         BuildTypeMethods ();
146                         
147                         if (mappings != null)
148                         {
149                                 // Build all the serializers at once
150                                 XmlMapping[] maps = (XmlMapping[]) mappings.ToArray(typeof(XmlMapping));
151                                 serializers = XmlSerializer.FromMappings (maps);
152                         }
153                 }
154                 
155                 //
156                 // Extract all method information
157                 //
158                 protected virtual void BuildTypeMethods ()
159                 {
160                         bool isClientProxy = typeof(WebClientProtocol).IsAssignableFrom (Type);
161
162                         ArrayList metStubs = new ArrayList ();
163                         foreach (LogicalMethodInfo mi in logicalType.LogicalMethods)
164                         {
165                                 if (!isClientProxy && mi.CustomAttributeProvider.GetCustomAttributes (typeof(WebMethodAttribute), true).Length == 0)
166                                         continue;
167                                         
168                                 MethodStubInfo msi = CreateMethodStubInfo (this, mi, isClientProxy);
169
170                                 if (msi == null)
171                                         continue;
172
173                                 if (name_to_method.ContainsKey (msi.Name)) {
174                                         string msg = "Both " + msi.MethodInfo.ToString () + " and " + GetMethod (msi.Name).MethodInfo + " use the message name '" + msi.Name + "'. ";
175                                         msg += "Use the MessageName property of WebMethod custom attribute to specify unique message names for the methods";
176                                         throw new InvalidOperationException (msg);
177                                 }
178                                 
179                                 name_to_method [msi.Name] = msi;
180                                 metStubs.Add (msi);
181                         }
182                         methods = (MethodStubInfo[]) metStubs.ToArray (typeof (MethodStubInfo));
183                 }
184                 
185                 protected virtual MethodStubInfo CreateMethodStubInfo (TypeStubInfo typeInfo, LogicalMethodInfo methodInfo, bool isClientProxy)
186                 {
187                         return new MethodStubInfo (typeInfo, methodInfo);
188                 }
189                 
190                 public MethodStubInfo GetMethod (string name)
191                 {
192                         return (MethodStubInfo) name_to_method [name];
193                 }
194
195                 public MethodStubInfo[] Methods
196                 {
197                         get { return methods; }
198                 }
199                 
200                 internal ArrayList Bindings
201                 {
202                         get { return bindings; }
203                 }
204                 
205                 internal void AddBinding (BindingInfo info)
206                 {
207                         bindings.Add (info);
208                 }
209                 
210                 internal BindingInfo GetBinding (string name)
211                 {
212                         if (name == null || name.Length == 0) return (BindingInfo) bindings[0];
213                         
214                         for (int n=1; n<bindings.Count; n++)
215                                 if (((BindingInfo)bindings[n]).Name == name) return (BindingInfo)bindings[n];
216                         return null;
217                 }
218         }
219         
220         internal class BindingInfo
221         {
222                 public BindingInfo (WebServiceBindingAttribute at, string ns)
223                 {
224                         Name = at.Name;
225                         Namespace = at.Namespace;
226                         if (Namespace == "") Namespace = ns;
227                         Location = at.Location;
228                 }
229                 
230                 public BindingInfo (string name, string ns)
231                 {
232                         Name = name;
233                         Namespace = ns;
234                 }
235                 
236                 public string Name;
237                 public string Namespace;
238                 public string Location;
239         }
240
241
242         //
243         // This class has information abou a web service. Through providess
244         // access to the TypeStubInfo instances for each protocol.
245         //
246         internal class LogicalTypeInfo
247         {
248                 LogicalMethodInfo[] logicalMethods;
249
250                 internal string WebServiceName;
251                 internal string WebServiceNamespace;
252                 string WebServiceLiteralNamespace;
253                 string WebServiceEncodedNamespace;
254                 internal string WebServiceAbstractNamespace;
255                 internal string Description;
256                 internal Type Type;
257                 bool useEncoded;
258
259                 TypeStubInfo soapProtocol;
260                 TypeStubInfo httpGetProtocol;
261                 TypeStubInfo httpPostProtocol;
262                 
263                 public LogicalTypeInfo (Type t)
264                 {
265                         this.Type = t;
266
267                         object [] o = Type.GetCustomAttributes (typeof (WebServiceAttribute), false);
268                         if (o.Length == 1){
269                                 WebServiceAttribute a = (WebServiceAttribute) o [0];
270                                 WebServiceName = (a.Name != string.Empty) ? a.Name : Type.Name;
271                                 WebServiceNamespace = (a.Namespace != string.Empty) ? a.Namespace : WebServiceAttribute.DefaultNamespace;
272                                 Description = a.Description;
273                         } else {
274                                 WebServiceName = Type.Name;
275                                 WebServiceNamespace = WebServiceAttribute.DefaultNamespace;
276                         }
277                         
278                         // Determine the namespaces for literal and encoded schema types
279                         
280                         useEncoded = false;
281                         
282                         o = t.GetCustomAttributes (typeof(SoapDocumentServiceAttribute), true);
283                         if (o.Length > 0) {
284                                 SoapDocumentServiceAttribute at = (SoapDocumentServiceAttribute) o[0];
285                                 useEncoded = (at.Use == SoapBindingUse.Encoded);
286                         }
287                         else if (t.GetCustomAttributes (typeof(SoapRpcServiceAttribute), true).Length > 0)
288                                 useEncoded = true;
289                         
290                         string sep = WebServiceNamespace.EndsWith ("/") ? "" : "/";
291                         
292                         if (useEncoded) {
293                                 WebServiceEncodedNamespace = WebServiceNamespace;
294                                 WebServiceLiteralNamespace = WebServiceNamespace + sep + "literalTypes";
295                         }
296                         else {
297                                 WebServiceEncodedNamespace = WebServiceNamespace + sep + "encodedTypes";
298                                 WebServiceLiteralNamespace = WebServiceNamespace;
299                         }
300                         
301                         WebServiceAbstractNamespace = WebServiceNamespace + sep + "AbstractTypes";
302                         
303                         MethodInfo [] type_methods = Type.GetMethods (BindingFlags.Instance | BindingFlags.Public);
304                         logicalMethods = LogicalMethodInfo.Create (type_methods, LogicalMethodTypes.Sync);
305                 }
306                 
307                 public LogicalMethodInfo[] LogicalMethods
308                 {
309                         get { return logicalMethods; }
310                 }
311                 
312                 public TypeStubInfo GetTypeStub (string protocolName)
313                 {
314                         lock (this)
315                         {
316                                 switch (protocolName)
317                                 {
318                                         case "Soap": 
319                                                 if (soapProtocol == null) soapProtocol = CreateTypeStubInfo (typeof(SoapTypeStubInfo));
320                                                 return soapProtocol;
321                                         case "HttpGet":
322                                                 if (httpGetProtocol == null) httpGetProtocol = CreateTypeStubInfo (typeof(HttpGetTypeStubInfo));
323                                                 return httpGetProtocol;
324                                         case "HttpPost":
325                                                 if (httpPostProtocol == null) httpPostProtocol = CreateTypeStubInfo (typeof(HttpPostTypeStubInfo));
326                                                 return httpPostProtocol;
327                                 }
328                         }
329                         throw new InvalidOperationException ("Protocol " + protocolName + " not supported");
330                 }
331                 
332                 TypeStubInfo CreateTypeStubInfo (Type type)
333                 {
334                         TypeStubInfo tsi = (TypeStubInfo) Activator.CreateInstance (type, new object[] {this});
335                         tsi.Initialize ();
336                         return tsi;
337                 }
338                 
339                 public string GetWebServiceLiteralNamespace (string baseNamespace)
340                 {
341                         if (useEncoded) {
342                                 string sep = baseNamespace.EndsWith ("/") ? "" : "/";
343                                 return baseNamespace + sep + "literalTypes";
344                         }
345                         else
346                                 return baseNamespace;
347                 }
348
349                 public string GetWebServiceEncodedNamespace (string baseNamespace)
350                 {
351                         if (useEncoded)
352                                 return baseNamespace;
353                         else {
354                                 string sep = baseNamespace.EndsWith ("/") ? "" : "/";
355                                 return baseNamespace + sep + "encodedTypes";
356                         }
357                 }
358
359                 public string GetWebServiceNamespace (string baseNamespace, SoapBindingUse use)
360                 {
361                         if (use == SoapBindingUse.Literal) return GetWebServiceLiteralNamespace (baseNamespace);
362                         else return GetWebServiceEncodedNamespace (baseNamespace);
363                 }
364                 
365         }
366
367         //
368         // Manages type stubs
369         //
370         internal class TypeStubManager 
371         {
372                 static Hashtable type_to_manager;
373                 
374                 static TypeStubManager ()
375                 {
376                         type_to_manager = new Hashtable ();
377                 }
378
379                 static internal TypeStubInfo GetTypeStub (Type t, string protocolName)
380                 {
381                         LogicalTypeInfo tm = GetLogicalTypeInfo (t);
382                         return tm.GetTypeStub (protocolName);
383                 }
384                 
385                 //
386                 // This needs to be thread safe
387                 //
388                 static internal LogicalTypeInfo GetLogicalTypeInfo (Type t)
389                 {
390                         lock (type_to_manager)
391                         {
392                                 LogicalTypeInfo tm = (LogicalTypeInfo) type_to_manager [t];
393         
394                                 if (tm != null)
395                                         return tm;
396
397                                 tm = new LogicalTypeInfo (t);
398                                 type_to_manager [t] = tm;
399
400                                 return tm;
401                         }
402                 }
403         }
404 }