* ChannelServices.cs: ExceptionFilterSink should be internal.
[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 using System.Collections;
12 using System.Reflection;
13 using System.Runtime.Remoting;
14 using System.Runtime.Remoting.Channels;
15 using System.Runtime.Remoting.Messaging;
16 using System.Runtime.Remoting.Contexts;
17
18 namespace System.Runtime.Remoting
19 {
20         [Serializable]
21         internal class ChannelInfo : IChannelInfo
22         {
23                 object [] channelData = null;
24
25                 public ChannelInfo ()
26                 {
27                         channelData = ChannelServices.GetCurrentChannelInfo ();
28                 }
29
30                 public ChannelInfo (object remoteChannelData)
31                 {
32                         channelData = new object[] { remoteChannelData };
33                 }
34                 
35                 public object[] ChannelData 
36                 {
37                         get {
38                                 return channelData;
39                         }
40                         
41                         set {
42                                 channelData = value;
43                         }
44                 }
45         }
46 }
47
48 namespace System.Runtime.Remoting.Channels
49 {
50         public sealed class ChannelServices
51         {
52                 private static ArrayList registeredChannels = new ArrayList ();
53                 private static ArrayList delayedClientChannels = new ArrayList ();
54                 
55                 private static CrossContextChannel _crossContextSink = new CrossContextChannel();
56                 
57                 internal static string CrossContextUrl = "__CrossContext";
58
59                 private ChannelServices ()
60                 {
61                 }
62
63                 internal static CrossContextChannel CrossContextChannel
64                 {
65                         get { return _crossContextSink; }
66                 }
67
68                 internal static IMessageSink CreateClientChannelSinkChain(string url, object remoteChannelData, out string objectUri)
69                 {
70                         // Locate a channel that can parse the url. This channel will be used to
71                         // create the sink chain.
72
73                         object[] channelDataArray = (object[])remoteChannelData;
74
75                         lock (registeredChannels.SyncRoot)
76                         {
77                                 // First of all, try registered channels
78                                 foreach (IChannel c in registeredChannels) 
79                                 {
80                                         IChannelSender sender = c as IChannelSender;
81                                         if (sender == null) continue;
82         
83                                         IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
84                                         if (sink != null) return sink;
85                                 }
86                                 
87                                 // Not found. Try now creation delayed channels
88                                 RemotingConfiguration.LoadDefaultDelayedChannels ();
89                                 foreach (IChannelSender sender in delayedClientChannels) 
90                                 {
91                                         IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
92                                         if (sink != null) {
93                                                 delayedClientChannels.Remove (sender);
94                                                 RegisterChannel (sender);
95                                                 return sink;
96                                         }
97                                 }
98                         }
99                         
100                         objectUri = null;
101                         return null;
102                 }
103                 
104                 internal static IMessageSink CreateClientChannelSinkChain (IChannelSender sender, string url, object[] channelDataArray, out string objectUri)
105                 {
106                         objectUri = null;
107                         if (channelDataArray == null) {
108                                 return sender.CreateMessageSink (url, null, out objectUri);
109                         }
110                         else {
111                                 foreach (object data in channelDataArray) {
112                                         IMessageSink sink = sender.CreateMessageSink (url, data, out objectUri);
113                                         if (sink != null) return sink;          
114                                 }
115                         }
116                         return null;
117                 }
118                 
119                 public static IChannel[] RegisteredChannels
120                 {
121                         get {
122                                 lock (registeredChannels.SyncRoot)
123                                 {
124                                         IChannel[] channels = new IChannel[registeredChannels.Count];
125         
126                                         for (int i = 0; i < registeredChannels.Count; i++)
127                                                 channels[i] = (IChannel) registeredChannels[i];
128         
129                                         return channels;
130                                 }
131                         }
132                 }
133
134                 public static IServerChannelSink CreateServerChannelSinkChain (
135                         IServerChannelSinkProvider provider, IChannelReceiver channel)
136             {
137                         IServerChannelSinkProvider tmp = provider;
138                         while (tmp.Next != null) tmp = tmp.Next;
139                         tmp.Next = new ServerDispatchSinkProvider ();
140
141                         // Every provider has to call CreateSink() of its next provider
142                         return  provider.CreateSink (channel);
143                 }
144
145                 public static ServerProcessing DispatchMessage (
146                         IServerChannelSinkStack sinkStack,
147                         IMessage msg,
148                         out IMessage replyMsg)
149                 {
150                         if (msg == null) throw new ArgumentNullException ("msg");
151                         
152                         // Async processing is not done here because there isn't any way
153                         // to know if a message is to be dispatched sync or asynchronously.
154
155                         replyMsg = SyncDispatchMessage (msg);
156
157                         if (RemotingServices.IsOneWay (((IMethodMessage) msg).MethodBase))
158                                 return ServerProcessing.OneWay;
159                         else
160                                 return ServerProcessing.Complete;
161                 }
162
163                 public static IChannel GetChannel (string name)
164                 {
165                         lock (registeredChannels.SyncRoot)
166                         {
167                                 foreach (IChannel chnl in registeredChannels) {
168                                         if (chnl.ChannelName == name && !(chnl is CrossAppDomainChannel)) return chnl;
169                                 }
170                                 return null;
171                         }
172                 }
173
174                 public static IDictionary GetChannelSinkProperties (object obj)
175                 {
176                         if (!RemotingServices.IsTransparentProxy (obj))
177                                 throw new ArgumentException ("obj must be a proxy","obj");
178                                 
179                         ClientIdentity ident = (ClientIdentity) RemotingServices.GetRealProxy (obj).ObjectIdentity;
180                         IMessageSink sink = ident.ChannelSink;
181                         ArrayList dics = new ArrayList ();
182                         
183                         while (sink != null && !(sink is IClientChannelSink))
184                                 sink = sink.NextSink;
185
186                         if (sink == null)
187                                 return new Hashtable ();
188
189                         IClientChannelSink csink = sink as IClientChannelSink;
190                         while (csink != null)
191                         {
192                                 dics.Add (csink.Properties);
193                                 csink = csink.NextChannelSink;
194                         }
195
196                         IDictionary[] adics = (IDictionary[]) dics.ToArray (typeof(IDictionary[]));
197                         return new AggregateDictionary (adics);
198                 }
199
200                 public static string[] GetUrlsForObject (MarshalByRefObject obj)
201                 {
202                         string uri = RemotingServices.GetObjectUri (obj);
203                         if (uri == null) return new string [0];
204
205                         ArrayList list = new ArrayList ();
206
207                         lock (registeredChannels.SyncRoot)
208                         {
209                                 foreach (object chnl_obj in registeredChannels) {
210                                         if (chnl_obj is CrossAppDomainChannel) continue;
211                                         
212                                         IChannelReceiver chnl = chnl_obj as IChannelReceiver;
213         
214                                         if (chnl != null)
215                                                 list.AddRange (chnl.GetUrlsForUri (uri));
216                                 }
217                         }
218                         
219                         return  (string[]) list.ToArray (typeof(string));
220                 }
221
222                 public static void RegisterChannel (IChannel chnl)
223                 {
224                         // Put the channel in the correct place according to its priority.
225                         // Since there are not many channels, a linear search is ok.
226
227                         lock (registeredChannels.SyncRoot)
228                         {
229                                 int pos = -1;
230                                 for (int n = 0; n < registeredChannels.Count; n++) 
231                                 {
232                                         IChannel regc = (IChannel) registeredChannels[n];
233                                         
234                                         if (regc.ChannelName == chnl.ChannelName)
235                                                 throw new RemotingException ("Channel " + regc.ChannelName + " already registered");
236                                                 
237                                         if (regc.ChannelPriority < chnl.ChannelPriority && pos==-1)
238                                                 pos = n;
239                                 }
240                                 
241                                 if (pos != -1) registeredChannels.Insert (pos, chnl);
242                                 else registeredChannels.Add (chnl);
243                                 
244                                 IChannelReceiver receiver = chnl as IChannelReceiver;
245                                 if (receiver != null) receiver.StartListening (null);
246                         }
247                 }
248
249                 internal static void RegisterChannelConfig (ChannelData channel)
250                 {
251                         IServerChannelSinkProvider serverSinks = null;
252                         IClientChannelSinkProvider clientSinks = null;
253                         
254                         // Create server providers
255                         for (int n=channel.ServerProviders.Count-1; n>=0; n--)
256                         {
257                                 ProviderData prov = channel.ServerProviders[n] as ProviderData;
258                                 IServerChannelSinkProvider sinkp = (IServerChannelSinkProvider) CreateProvider (prov);
259                                 sinkp.Next = serverSinks;
260                                 serverSinks = sinkp;
261                         }
262                         
263                         // Create client providers
264                         for (int n=channel.ClientProviders.Count-1; n>=0; n--)
265                         {
266                                 ProviderData prov = channel.ClientProviders[n] as ProviderData;
267                                 IClientChannelSinkProvider sinkp = (IClientChannelSinkProvider) CreateProvider (prov);
268                                 sinkp.Next = clientSinks;
269                                 clientSinks = sinkp;
270                         }
271
272                         // Create the channel
273                         
274                         Type type = Type.GetType (channel.Type);
275                         if (type == null) throw new RemotingException ("Type '" + channel.Type + "' not found");
276                         
277                         Object[] parms;                 
278                         Type[] signature;                       
279                         bool clienc = typeof (IChannelSender).IsAssignableFrom (type);
280                         bool serverc = typeof (IChannelReceiver).IsAssignableFrom (type);
281                         
282                         if (clienc && serverc) {
283                                 signature = new Type [] {typeof(IDictionary), typeof(IClientChannelSinkProvider), typeof(IServerChannelSinkProvider)};
284                                 parms = new Object[] {channel.CustomProperties, clientSinks, serverSinks};
285                         }
286                         else if (clienc) {
287                                 signature = new Type [] {typeof(IDictionary), typeof(IClientChannelSinkProvider)};
288                                 parms = new Object[] {channel.CustomProperties, clientSinks};
289                         }
290                         else if (serverc) {
291                                 signature = new Type [] {typeof(IDictionary), typeof(IServerChannelSinkProvider)};
292                                 parms = new Object[] {channel.CustomProperties, serverSinks};
293                         }
294                         else
295                                 throw new RemotingException (type + " is not a valid channel type");
296                                 
297                         ConstructorInfo ctor = type.GetConstructor (signature);
298                         if (ctor == null)
299                                 throw new RemotingException (type + " does not have a valid constructor");
300
301                         IChannel ch;
302                         try
303                         {
304                                 ch = (IChannel) ctor.Invoke (parms);
305                         }
306                         catch (TargetInvocationException ex)
307                         {
308                                 throw ex.InnerException;
309                         }
310                         
311                         lock (registeredChannels.SyncRoot)
312                         {
313                                 if (channel.DelayLoadAsClientChannel == "true" && !(ch is IChannelReceiver))
314                                         delayedClientChannels.Add (ch);
315                                 else
316                                         RegisterChannel (ch);
317                         }
318                 }
319                 
320                 static object CreateProvider (ProviderData prov)
321                 {
322                         Type pvtype = Type.GetType (prov.Type);
323                         if (pvtype == null) throw new RemotingException ("Type '" + prov.Type + "' not found");
324                         Object[] pvparms = new Object[] {prov.CustomProperties, prov.CustomData};
325                         
326                         try
327                         {
328                                 return Activator.CreateInstance (pvtype, pvparms);
329                         }
330                         catch (Exception ex)
331                         {
332                                 if (ex is TargetInvocationException) ex = ((TargetInvocationException)ex).InnerException;
333                                 throw new RemotingException ("An instance of provider '" + pvtype + "' could not be created: " + ex.Message);
334                         }
335                 }
336
337                 public static IMessage SyncDispatchMessage (IMessage msg)
338                 {
339                         IMessage ret = CheckIncomingMessage (msg);
340                         if (ret != null) return CheckReturnMessage (msg, ret);
341                         ret = _crossContextSink.SyncProcessMessage (msg);
342                         return CheckReturnMessage (msg, ret);
343                 }
344
345                 public static IMessageCtrl AsyncDispatchMessage (IMessage msg, IMessageSink replySink)
346                 {
347                         IMessage ret = CheckIncomingMessage (msg);
348                         if (ret != null) {
349                                 replySink.SyncProcessMessage (CheckReturnMessage (msg, ret));
350                                 return null;
351                         }
352                         
353 #if NET_1_1
354                         if (RemotingConfiguration.CustomErrorsEnabled (IsLocalCall (msg)))
355                                 replySink = new ExceptionFilterSink (msg, replySink);
356 #endif
357                         
358                         return _crossContextSink.AsyncProcessMessage (msg, replySink);          
359                 }
360                 
361                 static ReturnMessage CheckIncomingMessage (IMessage msg)
362                 {
363                         IMethodMessage call = (IMethodMessage)msg;
364                         ServerIdentity identity = RemotingServices.GetIdentityForUri (call.Uri) as ServerIdentity;
365
366                         if (identity == null) 
367                                 return new ReturnMessage (new RemotingException ("No receiver for uri " + call.Uri), (IMethodCallMessage) msg);
368
369                         RemotingServices.SetMessageTargetIdentity (msg, identity);
370                         return null;
371                 }
372
373                 internal static IMessage CheckReturnMessage (IMessage callMsg, IMessage retMsg)
374                 {
375 #if NET_1_1
376                         IMethodReturnMessage ret = retMsg as IMethodReturnMessage;
377                         if (ret != null && ret.Exception != null)
378                         {
379                                 if (RemotingConfiguration.CustomErrorsEnabled (IsLocalCall (callMsg)))
380                                 {
381                                         Exception ex = new Exception ("Server encountered an internal error. For more information, turn off customErrors in the server's .config file.");
382                                         retMsg = new MethodResponse (ex, (IMethodCallMessage)callMsg);
383                                 }
384                         }
385 #endif
386                         return retMsg;
387                 }
388                 
389                 static bool IsLocalCall (IMessage callMsg)
390                 {
391                         return true;
392                         
393 /*                      How can I know if a call is local?!?
394                         
395                         object isLocal = callMsg.Properties ["__isLocalCall"];
396                         if (isLocal == null) return false;
397                         return (bool)isLocal;
398 */
399                 }
400
401                 public static void UnregisterChannel (IChannel chnl)
402                 {
403                         if (chnl == null)
404                                 throw new ArgumentNullException ();
405                                 
406                         lock (registeredChannels.SyncRoot)
407                         {
408                                 if (!registeredChannels.Contains ((object) chnl))
409                                         throw new RemotingException ();
410         
411                                 registeredChannels.Remove ((object) chnl);
412         
413                                 IChannelReceiver chnlReceiver = chnl as IChannelReceiver;
414                                 if(chnlReceiver != null)
415                                         chnlReceiver.StopListening(null);
416                         }
417                 }
418
419                 internal static object [] GetCurrentChannelInfo ()
420                 {
421                         ArrayList list = new ArrayList ();
422                         
423                         lock (registeredChannels.SyncRoot)
424                         {
425                                 foreach (object chnl_obj in registeredChannels) {
426                                         IChannelReceiver chnl = chnl_obj as IChannelReceiver;
427                                 
428                                         if (chnl != null) {
429                                                 object chnl_data = chnl.ChannelData;
430                                                 if (chnl_data != null)
431                                                         list.Add (chnl_data);
432                                         }
433                                 }
434                         }
435                         
436                         return  list.ToArray ();
437                 }
438         }
439         
440         internal class ExceptionFilterSink: IMessageSink
441         {
442                 IMessageSink _next;
443                 IMessage _call;
444                 
445                 public ExceptionFilterSink (IMessage call, IMessageSink next)
446                 {
447                         _call = call;
448                         _next = next;
449                 }
450                 
451                 public IMessage SyncProcessMessage (IMessage msg)
452                 {
453                         return _next.SyncProcessMessage (ChannelServices.CheckReturnMessage (_call, msg));
454                 }
455
456                 public IMessageCtrl AsyncProcessMessage (IMessage msg, IMessageSink replySink)
457                 {
458                         throw new InvalidOperationException();
459                 }
460
461                 public IMessageSink NextSink 
462                 { 
463                         get { return _next; }
464                 }
465         }
466 }