Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Channels / ChannelServices.cs
1 //
2 // System.Runtime.Remoting.Channels.ChannelServices.cs
3 //
4 // Author: Rodrigo Moya (rodrigo@ximian.com)
5 //         Dietmar Maurer (dietmar@ximian.com)
6 //         Lluis Sanchez Gual (lluis@ideary.com)
7 //
8 // 2002 (C) Copyright, Ximian, Inc.
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Reflection;
37 using System.Runtime.Remoting;
38 using System.Runtime.Remoting.Channels;
39 using System.Runtime.Remoting.Messaging;
40 using System.Runtime.Remoting.Contexts;
41
42 namespace System.Runtime.Remoting
43 {
44         [Serializable]
45         internal class ChannelInfo : IChannelInfo
46         {
47                 object [] channelData = null;
48
49                 public ChannelInfo ()
50                 {
51                         channelData = ChannelServices.GetCurrentChannelInfo ();
52                 }
53
54                 public ChannelInfo (object remoteChannelData)
55                 {
56                         channelData = new object[] { remoteChannelData };
57                 }
58                 
59                 public object[] ChannelData 
60                 {
61                         get {
62                                 return channelData;
63                         }
64                         
65                         set {
66                                 channelData = value;
67                         }
68                 }
69         }
70 }
71
72 namespace System.Runtime.Remoting.Channels
73 {
74         [System.Runtime.InteropServices.ComVisible (true)]
75         public sealed class ChannelServices
76         {
77                 private static ArrayList registeredChannels = new ArrayList ();
78                 private static ArrayList delayedClientChannels = new ArrayList ();
79                 
80                 private static CrossContextChannel _crossContextSink = new CrossContextChannel();
81                 
82                 internal static string CrossContextUrl = "__CrossContext";
83
84                 private ChannelServices ()
85                 {
86                 }
87
88                 internal static CrossContextChannel CrossContextChannel
89                 {
90                         get { return _crossContextSink; }
91                 }
92
93                 internal static IMessageSink CreateClientChannelSinkChain(string url, object remoteChannelData, out string objectUri)
94                 {
95                         // Locate a channel that can parse the url. This channel will be used to
96                         // create the sink chain.
97
98                         object[] channelDataArray = (object[])remoteChannelData;
99
100                         lock (registeredChannels.SyncRoot)
101                         {
102                                 // First of all, try registered channels
103                                 foreach (IChannel c in registeredChannels) 
104                                 {
105                                         IChannelSender sender = c as IChannelSender;
106                                         if (sender == null) continue;
107         
108                                         IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
109                                         if (sink != null) return sink;
110                                 }
111                                 
112                                 // Not found. Try now creation delayed channels
113                                 RemotingConfiguration.LoadDefaultDelayedChannels ();
114                                 foreach (IChannelSender sender in delayedClientChannels) 
115                                 {
116                                         IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
117                                         if (sink != null) {
118                                                 delayedClientChannels.Remove (sender);
119                                                 RegisterChannel (sender);
120                                                 return sink;
121                                         }
122                                 }
123                         }
124                         
125                         objectUri = null;
126                         return null;
127                 }
128                 
129                 internal static IMessageSink CreateClientChannelSinkChain (IChannelSender sender, string url, object[] channelDataArray, out string objectUri)
130                 {
131                         objectUri = null;
132                         if (channelDataArray == null) {
133                                 return sender.CreateMessageSink (url, null, out objectUri);
134                         }
135                         else {
136                                 foreach (object data in channelDataArray) {
137                                         IMessageSink sink;
138
139                                         if (data is IChannelDataStore) {
140                                                 // Don't provide the url in this case, since some channels won't
141                                                 // check the channelData parameter if the url is not null.
142                                                 sink = sender.CreateMessageSink (null, data, out objectUri);
143                                         } else {
144                                                 sink = sender.CreateMessageSink (url, data, out objectUri);
145                                         }
146                                         if (sink != null) return sink;          
147                                 }
148                         }
149                         return null;
150                 }
151                 
152                 public static IChannel[] RegisteredChannels
153                 {
154                         get {
155                                 lock (registeredChannels.SyncRoot)
156                                 {
157                                         var list = new List<IChannel> ();
158                                         
159                                         for (int i = 0; i < registeredChannels.Count; i++) {
160                                                 IChannel ch = (IChannel) registeredChannels[i];
161                                                 if (ch is CrossAppDomainChannel) continue;
162                                                 list.Add (ch);
163                                         }
164
165                                         return list.ToArray ();
166                                 }
167                         }
168                 }
169
170                 public static IServerChannelSink CreateServerChannelSinkChain (
171                         IServerChannelSinkProvider provider, IChannelReceiver channel)
172             {
173                         IServerChannelSinkProvider tmp = provider;
174                         while (tmp.Next != null) tmp = tmp.Next;
175                         tmp.Next = new ServerDispatchSinkProvider ();
176
177                         // Every provider has to call CreateSink() of its next provider
178                         return  provider.CreateSink (channel);
179                 }
180
181                 public static ServerProcessing DispatchMessage (
182                         IServerChannelSinkStack sinkStack,
183                         IMessage msg,
184                         out IMessage replyMsg)
185                 {
186                         if (msg == null) throw new ArgumentNullException ("msg");
187                         
188                         // Async processing is not done here because there isn't any way
189                         // to know if a message is to be dispatched sync or asynchronously.
190
191                         replyMsg = SyncDispatchMessage (msg);
192
193                         if (RemotingServices.IsOneWay (((IMethodMessage) msg).MethodBase))
194                                 return ServerProcessing.OneWay;
195                         else
196                                 return ServerProcessing.Complete;
197                 }
198
199                 public static IChannel GetChannel (string name)
200                 {
201                         lock (registeredChannels.SyncRoot)
202                         {
203                                 foreach (IChannel chnl in registeredChannels) {
204                                         if (chnl.ChannelName == name && !(chnl is CrossAppDomainChannel)) return chnl;
205                                 }
206                                 return null;
207                         }
208                 }
209
210                 public static IDictionary GetChannelSinkProperties (object obj)
211                 {
212                         if (!RemotingServices.IsTransparentProxy (obj))
213                                 throw new ArgumentException ("obj must be a proxy","obj");
214                                 
215                         ClientIdentity ident = (ClientIdentity) RemotingServices.GetRealProxy (obj).ObjectIdentity;
216                         IMessageSink sink = ident.ChannelSink;
217                         var dics = new List<IDictionary> ();
218                         
219                         while (sink != null && !(sink is IClientChannelSink))
220                                 sink = sink.NextSink;
221
222                         if (sink == null)
223                                 return new Hashtable ();
224
225                         IClientChannelSink csink = sink as IClientChannelSink;
226                         while (csink != null)
227                         {
228                                 dics.Add (csink.Properties);
229                                 csink = csink.NextChannelSink;
230                         }
231
232                         IDictionary[] adics = dics.ToArray ();
233                         return new AggregateDictionary (adics);
234                 }
235
236                 public static string[] GetUrlsForObject (MarshalByRefObject obj)
237                 {
238                         string uri = RemotingServices.GetObjectUri (obj);
239                         if (uri == null) return new string [0];
240
241                         var list = new List<string> ();
242
243                         lock (registeredChannels.SyncRoot)
244                         {
245                                 foreach (object chnl_obj in registeredChannels) {
246                                         if (chnl_obj is CrossAppDomainChannel) continue;
247                                         
248                                         IChannelReceiver chnl = chnl_obj as IChannelReceiver;
249         
250                                         if (chnl != null)
251                                                 list.AddRange (chnl.GetUrlsForUri (uri));
252                                 }
253                         }
254                         
255                         return list.ToArray ();
256                 }
257
258                 [Obsolete ("Use RegisterChannel(IChannel,Boolean)")]
259                 public static void RegisterChannel (IChannel chnl)
260                 {
261                         RegisterChannel (chnl, false);
262                 }
263
264                 public static void RegisterChannel (IChannel chnl, bool ensureSecurity)
265                 {
266                         if (chnl == null)
267                                 throw new ArgumentNullException ("chnl");
268
269                         if (ensureSecurity) {
270                                 ISecurableChannel securable = chnl as ISecurableChannel;
271                                 if (securable == null)
272                                         throw new RemotingException (String.Format ("Channel {0} is not securable while ensureSecurity is specified as true", chnl.ChannelName));
273                                 securable.IsSecured = true;
274                         }
275                         
276                         // Put the channel in the correct place according to its priority.
277                         // Since there are not many channels, a linear search is ok.
278
279                         lock (registeredChannels.SyncRoot)
280                         {
281                                 int pos = -1;
282                                 for (int n = 0; n < registeredChannels.Count; n++) 
283                                 {
284                                         IChannel regc = (IChannel) registeredChannels[n];
285                                         
286                                         if (regc.ChannelName == chnl.ChannelName && chnl.ChannelName != "")
287                                                 throw new RemotingException ("Channel " + regc.ChannelName + " already registered");
288                                                 
289                                         if (regc.ChannelPriority < chnl.ChannelPriority && pos==-1)
290                                                 pos = n;
291                                 }
292                                 
293                                 if (pos != -1) registeredChannels.Insert (pos, chnl);
294                                 else registeredChannels.Add (chnl);
295
296                                 IChannelReceiver receiver = chnl as IChannelReceiver;
297                                 if (receiver != null && oldStartModeTypes.Contains (chnl.GetType().ToString ()))
298                                         receiver.StartListening (null);
299                         }
300                 }
301
302                 internal static void RegisterChannelConfig (ChannelData channel)
303                 {
304                         IServerChannelSinkProvider serverSinks = null;
305                         IClientChannelSinkProvider clientSinks = null;
306                         
307                         // Create server providers
308                         for (int n=channel.ServerProviders.Count-1; n>=0; n--)
309                         {
310                                 ProviderData prov = channel.ServerProviders[n] as ProviderData;
311                                 IServerChannelSinkProvider sinkp = (IServerChannelSinkProvider) CreateProvider (prov);
312                                 sinkp.Next = serverSinks;
313                                 serverSinks = sinkp;
314                         }
315                         
316                         // Create client providers
317                         for (int n=channel.ClientProviders.Count-1; n>=0; n--)
318                         {
319                                 ProviderData prov = channel.ClientProviders[n] as ProviderData;
320                                 IClientChannelSinkProvider sinkp = (IClientChannelSinkProvider) CreateProvider (prov);
321                                 sinkp.Next = clientSinks;
322                                 clientSinks = sinkp;
323                         }
324
325                         // Create the channel
326                         
327                         Type type = Type.GetType (channel.Type);
328                         if (type == null) throw new RemotingException ("Type '" + channel.Type + "' not found");
329                         
330                         Object[] parms;                 
331                         Type[] signature;                       
332                         bool clienc = typeof (IChannelSender).IsAssignableFrom (type);
333                         bool serverc = typeof (IChannelReceiver).IsAssignableFrom (type);
334                         
335                         if (clienc && serverc) {
336                                 signature = new Type [] {typeof(IDictionary), typeof(IClientChannelSinkProvider), typeof(IServerChannelSinkProvider)};
337                                 parms = new Object[] {channel.CustomProperties, clientSinks, serverSinks};
338                         }
339                         else if (clienc) {
340                                 signature = new Type [] {typeof(IDictionary), typeof(IClientChannelSinkProvider)};
341                                 parms = new Object[] {channel.CustomProperties, clientSinks};
342                         }
343                         else if (serverc) {
344                                 signature = new Type [] {typeof(IDictionary), typeof(IServerChannelSinkProvider)};
345                                 parms = new Object[] {channel.CustomProperties, serverSinks};
346                         }
347                         else
348                                 throw new RemotingException (type + " is not a valid channel type");
349                                 
350                         ConstructorInfo ctor = type.GetConstructor (signature);
351                         if (ctor == null)
352                                 throw new RemotingException (type + " does not have a valid constructor");
353
354                         IChannel ch;
355                         try
356                         {
357                                 ch = (IChannel) ctor.Invoke (parms);
358                         }
359                         catch (TargetInvocationException ex)
360                         {
361                                 throw ex.InnerException;
362                         }
363                         
364                         lock (registeredChannels.SyncRoot)
365                         {
366                                 if (channel.DelayLoadAsClientChannel == "true" && !(ch is IChannelReceiver))
367                                         delayedClientChannels.Add (ch);
368                                 else
369                                         RegisterChannel (ch);
370                         }
371                 }
372                 
373                 static object CreateProvider (ProviderData prov)
374                 {
375                         Type pvtype = Type.GetType (prov.Type);
376                         if (pvtype == null) throw new RemotingException ("Type '" + prov.Type + "' not found");
377                         Object[] pvparms = new Object[] {prov.CustomProperties, prov.CustomData};
378                         
379                         try
380                         {
381                                 return Activator.CreateInstance (pvtype, pvparms);
382                         }
383                         catch (Exception ex)
384                         {
385                                 if (ex is TargetInvocationException) ex = ((TargetInvocationException)ex).InnerException;
386                                 throw new RemotingException ("An instance of provider '" + pvtype + "' could not be created: " + ex.Message);
387                         }
388                 }
389
390                 public static IMessage SyncDispatchMessage (IMessage msg)
391                 {
392                         IMessage ret = CheckIncomingMessage (msg);
393                         if (ret != null) return CheckReturnMessage (msg, ret);
394                         ret = _crossContextSink.SyncProcessMessage (msg);
395                         return CheckReturnMessage (msg, ret);
396                 }
397
398                 public static IMessageCtrl AsyncDispatchMessage (IMessage msg, IMessageSink replySink)
399                 {
400                         IMessage ret = CheckIncomingMessage (msg);
401                         if (ret != null) {
402                                 replySink.SyncProcessMessage (CheckReturnMessage (msg, ret));
403                                 return null;
404                         }
405                         
406                         if (RemotingConfiguration.CustomErrorsEnabled (IsLocalCall (msg)))
407                                 replySink = new ExceptionFilterSink (msg, replySink);
408                         
409                         return _crossContextSink.AsyncProcessMessage (msg, replySink);          
410                 }
411                 
412                 static ReturnMessage CheckIncomingMessage (IMessage msg)
413                 {
414                         IMethodMessage call = (IMethodMessage)msg;
415                         ServerIdentity identity = RemotingServices.GetIdentityForUri (call.Uri) as ServerIdentity;
416
417                         if (identity == null) 
418                                 return new ReturnMessage (new RemotingException ("No receiver for uri " + call.Uri), (IMethodCallMessage) msg);
419
420                         RemotingServices.SetMessageTargetIdentity (msg, identity);
421                         return null;
422                 }
423
424                 internal static IMessage CheckReturnMessage (IMessage callMsg, IMessage retMsg)
425                 {
426                         IMethodReturnMessage ret = retMsg as IMethodReturnMessage;
427                         if (ret != null && ret.Exception != null)
428                         {
429                                 if (RemotingConfiguration.CustomErrorsEnabled (IsLocalCall (callMsg)))
430                                 {
431                                         Exception ex = new Exception ("Server encountered an internal error. For more information, turn off customErrors in the server's .config file.");
432                                         retMsg = new MethodResponse (ex, (IMethodCallMessage)callMsg);
433                                 }
434                         }
435                         return retMsg;
436                 }
437                 
438                 static bool IsLocalCall (IMessage callMsg)
439                 {
440                         return true;
441                         
442 /*                      How can I know if a call is local?!?
443                         
444                         object isLocal = callMsg.Properties ["__isLocalCall"];
445                         if (isLocal == null) return false;
446                         return (bool)isLocal;
447 */
448                 }
449
450                 public static void UnregisterChannel (IChannel chnl)
451                 {
452                         if (chnl == null)
453                                 throw new ArgumentNullException ();
454                                 
455                         lock (registeredChannels.SyncRoot)
456                         {
457                                 for (int n=0; n<registeredChannels.Count; n++) 
458                                 {
459                                         if (registeredChannels [n] == (object)chnl) {
460                                                 registeredChannels.RemoveAt (n);
461                                                 IChannelReceiver chnlReceiver = chnl as IChannelReceiver;
462                                                 if(chnlReceiver != null)
463                                                         chnlReceiver.StopListening(null);
464                                                 return;
465                                         }
466                                 }
467                                 
468                                 throw new RemotingException ("Channel not registered");
469         
470                         }
471                 }
472
473                 internal static object [] GetCurrentChannelInfo ()
474                 {
475                         var list = new List<object> ();
476                         
477                         lock (registeredChannels.SyncRoot)
478                         {
479                                 foreach (object chnl_obj in registeredChannels) {
480                                         IChannelReceiver chnl = chnl_obj as IChannelReceiver;
481                                 
482                                         if (chnl != null) {
483                                                 object chnl_data = chnl.ChannelData;
484                                                 if (chnl_data != null)
485                                                         list.Add (chnl_data);
486                                         }
487                                 }
488                         }
489                         
490                         return list.ToArray ();
491                 }
492
493                 // Back compatibility fix. StartListener will be called for the types listed here               
494                 static IList oldStartModeTypes = new string[] {
495                         "Novell.Zenworks.Zmd.Public.UnixServerChannel",
496                         "Novell.Zenworks.Zmd.Public.UnixChannel"
497                 };
498         }
499         
500         internal class ExceptionFilterSink: IMessageSink
501         {
502                 IMessageSink _next;
503                 IMessage _call;
504                 
505                 public ExceptionFilterSink (IMessage call, IMessageSink next)
506                 {
507                         _call = call;
508                         _next = next;
509                 }
510                 
511                 public IMessage SyncProcessMessage (IMessage msg)
512                 {
513                         return _next.SyncProcessMessage (ChannelServices.CheckReturnMessage (_call, msg));
514                 }
515
516                 public IMessageCtrl AsyncProcessMessage (IMessage msg, IMessageSink replySink)
517                 {
518                         throw new InvalidOperationException();
519                 }
520
521                 public IMessageSink NextSink 
522                 { 
523                         get { return _next; }
524                 }
525         }
526 }