* RemotingServices.cs: Some small fixes.
[mono.git] / mcs / class / corlib / System.Runtime.Remoting / RemotingServices.cs
1 //
2 // System.Runtime.Remoting.RemotingServices.cs
3 //
4 // Authors:
5 //   Dietmar Maurer (dietmar@ximian.com)
6 //   Lluis Sanchez Gual (lluis@ideary.com)
7 //   Patrik Torstensson
8 //
9 // (C) 2001 Ximian, Inc.  http://www.ximian.com
10 //
11
12 using System;
13 using System.Reflection;
14 using System.Threading;
15 using System.Collections;
16 using System.Runtime.Remoting.Messaging;
17 using System.Runtime.Remoting.Proxies;
18 using System.Runtime.Remoting.Channels;
19 using System.Runtime.Remoting.Contexts;
20 using System.Runtime.Remoting.Activation;
21 using System.Runtime.Remoting.Lifetime;
22 using System.Runtime.CompilerServices;
23 using System.Runtime.Serialization;
24 using System.IO;
25
26 namespace System.Runtime.Remoting
27 {
28         public sealed class RemotingServices 
29         {
30                 // Holds the identities of the objects, using uri as index
31                 static Hashtable uri_hash = new Hashtable ();           
32
33                 internal static string app_id;
34                 static int next_id = 1;
35                 
36                 static RemotingServices ()
37                 {
38                         RegisterInternalChannels ();
39                         app_id = "/" + Guid.NewGuid().ToString().Replace('-', '_') + "/";
40                         CreateWellKnownServerIdentity (typeof(RemoteActivator), "RemoteActivationService.rem", WellKnownObjectMode.Singleton);
41                 }
42         
43                 private RemotingServices () {}
44
45                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
46                 internal extern static object InternalExecute (MonoMethod method, Object obj,
47                                                                Object[] parameters, out object [] out_args);
48
49                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
50                 public extern static bool IsTransparentProxy (object proxy);
51                 
52                 internal static IMethodReturnMessage InternalExecuteMessage (
53                         MarshalByRefObject target, IMethodCallMessage reqMsg)
54                 {
55                         ReturnMessage result;
56                         
57                         MonoMethod method = (MonoMethod) target.GetType().GetMethod(reqMsg.MethodName, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance, null, (Type[]) reqMsg.MethodSignature, null);
58
59                         try {
60                                 object [] out_args;
61                                 object rval = InternalExecute (method, target, reqMsg.Args, out out_args);
62                                 result = new ReturnMessage (rval, out_args, out_args.Length,
63                                                             reqMsg.LogicalCallContext, reqMsg);
64                         
65                         } catch (Exception e) {
66                                 result = new ReturnMessage (e, reqMsg);
67                         }
68
69                         return result;
70                 }
71
72                 public static IMethodReturnMessage ExecuteMessage (
73                         MarshalByRefObject target, IMethodCallMessage reqMsg)
74                 {
75                         if (IsTransparentProxy(target))
76                         {
77                                 // Message must go through all chain of sinks
78                                 RealProxy rp = GetRealProxy (target);
79                                 return (IMethodReturnMessage) rp.Invoke (reqMsg);
80                         }
81                         else    // Direct call
82                                 return InternalExecuteMessage (target, reqMsg);
83                 }
84
85                 public static object Connect (Type classToProxy, string url)
86                 {
87                         ObjRef objRef = new ObjRef (classToProxy, url, null);
88                         return GetRemoteObject (objRef, classToProxy);
89                 }
90
91                 public static object Connect (Type classToProxy, string url, object data)
92                 {
93                         ObjRef objRef = new ObjRef (classToProxy, url, data);
94                         return GetRemoteObject (objRef, classToProxy);
95                 }
96
97                 public static bool Disconnect (MarshalByRefObject obj)
98                 {
99                         if (obj == null) throw new ArgumentNullException ("obj");
100
101                         ServerIdentity identity;
102
103                         if (IsTransparentProxy (obj))
104                         {
105                                 // CBOs are always accessed through a proxy, even in the server, so
106                                 // for server CBOs it is ok to disconnect a proxy
107
108                                 RealProxy proxy = GetRealProxy(obj);
109                                 if (proxy.GetProxiedType().IsContextful && (proxy.ObjectIdentity is ServerIdentity))
110                                         identity = proxy.ObjectIdentity as ServerIdentity;
111                                 else
112                                         throw new ArgumentException ("The obj parameter is a proxy");
113                         }
114                         else
115                                 identity = obj.ObjectIdentity;
116
117                         if (identity == null || !identity.IsConnected)
118                                 return false;
119                         else
120                         {
121                                 LifetimeServices.StopTrackingLifetime (identity);
122                                 DisposeIdentity (identity);
123                                 return true;
124                         }
125                 }
126
127                 public static Type GetServerTypeForUri (string uri)
128                 {
129                         Identity ident = GetIdentityForUri (uri);
130                         if (ident == null) return null;
131                         return ident.ObjectType;
132                 }
133
134                 public static string GetObjectUri (MarshalByRefObject obj)
135                 {
136                         Identity ident = GetObjectIdentity(obj);
137                         if (ident is ClientIdentity) return ((ClientIdentity)ident).TargetUri;
138                         else if (ident != null) return ident.ObjectUri;
139                         else return null;
140                 }
141
142                 public static object Unmarshal (ObjRef objref)
143                 {
144                         return Unmarshal(objref, true);
145                 }
146
147                 public static object Unmarshal (ObjRef objref, bool fRefine)
148                 {
149                         // FIXME: use type name when fRefine==true
150
151                         Type classToProxy = fRefine ? objref.ServerType : typeof (MarshalByRefObject);
152                         if (classToProxy == null) classToProxy = typeof (MarshalByRefObject);
153
154                         if (objref.IsReferenceToWellKnow)
155                                 return GetRemoteObject(objref, classToProxy);
156                         else
157                         {
158                                 if (classToProxy.IsContextful)
159                                 {
160                                         // Look for a ProxyAttribute
161                                         ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (classToProxy, typeof(ProxyAttribute),true);
162                                         if (att != null)
163                                                 return att.CreateProxy (objref, classToProxy, null, null).GetTransparentProxy();
164                                 }
165                                 return GetProxyForRemoteObject (objref, classToProxy);
166                         }
167                 }
168
169                 public static ObjRef Marshal (MarshalByRefObject obj)
170                 {
171                         return Marshal (obj, null, null);
172                 }
173                 
174                 public static ObjRef Marshal (MarshalByRefObject obj, string uri)
175                 {
176                         return Marshal (obj, uri, null);
177                 }
178                 
179                 public static ObjRef Marshal (MarshalByRefObject obj, string uri, Type requested_type)
180                 {
181                         if (IsTransparentProxy (obj))
182                         {
183                                 RealProxy proxy = RemotingServices.GetRealProxy(obj);
184                                 Identity identity = proxy.ObjectIdentity;
185
186                                 if (identity != null)
187                                 {
188                                         if (identity.ObjectType.IsContextful && !identity.IsConnected)
189                                         {
190                                                 // Unregistered local contextbound object. Register now.
191                                                 ClientActivatedIdentity cboundIdentity = (ClientActivatedIdentity)identity;
192                                                 if (uri == null) uri = NewUri();
193                                                 cboundIdentity.ObjectUri = uri;
194                                                 RegisterServerIdentity (cboundIdentity);
195                                                 cboundIdentity.StartTrackingLifetime ((ILease)obj.InitializeLifetimeService());
196                                                 return cboundIdentity.CreateObjRef(requested_type);
197                                         }
198                                         else if (uri != null)
199                                                 throw new RemotingException ("It is not possible marshal a proxy of a remote object");
200
201                                         return proxy.ObjectIdentity.CreateObjRef(requested_type);
202                                 }
203                         }
204
205                         if (requested_type == null) requested_type = obj.GetType();
206
207                         if (uri == null) 
208                         {
209                                 uri = NewUri();
210                                 CreateClientActivatedServerIdentity (obj, requested_type, uri);
211                         }
212                         else
213                         {
214                                 ClientActivatedIdentity identity = GetIdentityForUri (uri) as ClientActivatedIdentity;
215                                 if (identity == null || obj != identity.GetServerObject()) 
216                                         CreateClientActivatedServerIdentity (obj, requested_type, uri);
217                         }
218
219                         return obj.CreateObjRef(requested_type);
220                 }
221
222                 static string NewUri ()
223                 {
224                         return app_id + Environment.TickCount + "_" + next_id++;
225                 }
226
227                 public static RealProxy GetRealProxy (object proxy)
228                 {
229                         if (!IsTransparentProxy(proxy)) throw new RemotingException("Cannot get the real proxy from an object that is not a transparent proxy");
230                         return (RealProxy)((TransparentProxy)proxy)._rp;
231                 }
232
233                 public static MethodBase GetMethodBaseFromMethodMessage(IMethodMessage msg)
234                 {
235                         Type type = Type.GetType (msg.TypeName);
236                         if (type == null)
237                                 throw new RemotingException ("Type '" + msg.TypeName + "' not found!");
238
239                         BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
240                         if (msg.MethodSignature == null)
241                                 return type.GetMethod (msg.MethodName, bflags);
242                         else
243                                 return type.GetMethod (msg.MethodName, bflags, null, (Type[]) msg.MethodSignature, null);
244                 }
245
246                 public static void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
247                 {
248                         if (obj == null) throw new ArgumentNullException ("obj");
249
250                         ObjRef oref = Marshal ((MarshalByRefObject)obj);
251                         oref.GetObjectData (info, context);
252                 }
253
254                 public static ObjRef GetObjRefForProxy(MarshalByRefObject obj)
255                 {
256                         Identity ident = GetObjectIdentity(obj);
257                         if (ident == null) return null;
258                         else return ident.CreateObjRef(null);
259                 }
260
261                 public static object GetLifetimeService (MarshalByRefObject obj)
262                 {
263                         if (obj == null) return null;
264                         return obj.GetLifetimeService ();
265                 }
266
267                 public static IMessageSink GetEnvoyChainForProxy (MarshalByRefObject obj)
268                 {
269                         if (IsTransparentProxy(obj))
270                                 return ((ClientIdentity)GetRealProxy (obj).ObjectIdentity).EnvoySink;
271                         else
272                                 throw new ArgumentException ("obj must be a proxy","obj");                      
273                 }
274
275                 public static void LogRemotingStage (int stage)
276                 {
277                         throw new NotImplementedException ();
278                 }
279
280                 [MonoTODO]
281                 public static string GetSessionIdForMethodMessage(IMethodMessage msg)
282                 {
283                         throw new NotImplementedException (); 
284                 }
285
286                 public static bool IsMethodOverloaded(IMethodMessage msg)
287                 {
288                         Type type = msg.MethodBase.DeclaringType;
289                         MemberInfo[] members = type.GetMember (msg.MethodName, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
290                         return members.Length > 1;
291                 }
292
293                 public static bool IsObjectOutOfAppDomain(object tp)
294                 {
295                         Identity ident = GetObjectIdentity((MarshalByRefObject)tp);
296                         if (ident != null) return !ident.IsFromThisAppDomain;
297                         else return false;
298                 }
299
300                 public static bool IsObjectOutOfContext(object tp)
301                 {
302                         ServerIdentity ident = GetObjectIdentity((MarshalByRefObject)tp) as ServerIdentity;
303                         if (ident != null) return ident.Context != System.Threading.Thread.CurrentContext;
304                         else return false;
305                 }
306
307                 public static bool IsOneWay(MethodBase method)
308                 {
309                         // TODO: use internal call for better performance
310                         object[] atts = method.GetCustomAttributes (typeof (OneWayAttribute), false);
311                         return atts.Length > 0;
312                 }
313
314                 public static bool IsAsyncMessage(IMessage msg)
315                 {
316                         if (! (msg is MonoMethodMessage)) return false;
317                         else if (((MonoMethodMessage)msg).IsAsync) return true;
318                         else if (IsOneWay (((MonoMethodMessage)msg).MethodBase)) return true;
319                         else return false;
320                 }
321
322                 public static void SetObjectUriForMarshal(MarshalByRefObject obj, string uri)
323                 {
324                         if (IsTransparentProxy (obj)) throw new RemotingException ("SetObjectUriForMarshal method should only be called for MarshalByRefObjects that exist in the current AppDomain.");
325                         Marshal (obj, uri);
326                 }
327
328                 #region Internal Methods
329                 
330                 internal static object CreateClientProxy (ActivatedClientTypeEntry entry, object[] activationAttributes)
331                 {
332                         if (entry.ContextAttributes != null || activationAttributes != null)
333                         {
334                                 ArrayList props = new ArrayList ();
335                                 if (entry.ContextAttributes != null) props.AddRange (entry.ContextAttributes);
336                                 if (activationAttributes != null) props.AddRange (activationAttributes);
337                                 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, props.ToArray ());
338                         }
339                         else
340                                 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, null);
341                 }
342         
343                 internal static object CreateClientProxy (Type objectType, string url, object[] activationAttributes)
344                 {
345                         string activationUrl = url + "/RemoteActivationService.rem";
346
347                         string objectUri;
348                         IMessageSink sink = GetClientChannelSinkChain (activationUrl, null, out objectUri);
349
350                         RemotingProxy proxy = new RemotingProxy (objectType, activationUrl, activationAttributes);
351                         return proxy.GetTransparentProxy();
352                 }
353         
354                 internal static object CreateClientProxy (WellKnownClientTypeEntry entry)
355                 {
356                         return Connect (entry.ObjectType, entry.ObjectUrl, null);
357                 }
358         
359                 internal static object CreateClientProxyForContextBound (Type type, object[] activationAttributes)
360                 {
361                         if (type.IsContextful)
362                         {
363                                 // Look for a ProxyAttribute
364                                 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (type, typeof(ProxyAttribute), true);
365                                 if (att != null)
366                                         return att.CreateInstance (type);
367                         }
368                         RemotingProxy proxy = new RemotingProxy (type, ChannelServices.CrossContextUrl, activationAttributes);
369                         return proxy.GetTransparentProxy();
370                 }
371         
372                 internal static Identity GetIdentityForUri (string uri)
373                 {
374                         lock (uri_hash)
375                         {
376                                 return (Identity)uri_hash [uri];
377                         }
378                 }
379
380                 internal static Identity GetObjectIdentity (MarshalByRefObject obj)
381                 {
382                         if (IsTransparentProxy(obj))
383                                 return GetRealProxy (obj).ObjectIdentity;
384                         else
385                                 return obj.ObjectIdentity;
386                 }
387
388                 internal static ClientIdentity GetOrCreateClientIdentity(ObjRef objRef, Type proxyType)
389                 {
390                         // This method looks for an identity for the given url. 
391                         // If an identity is not found, it creates the identity and 
392                         // assigns it a proxy to the remote object.
393
394                         // Creates the client sink chain for the given url or channelData.
395                         // It will also get the object uri from the url.
396
397                         object channelData = objRef.ChannelInfo != null ? objRef.ChannelInfo.ChannelData : null;
398                         string url = (channelData == null) ? objRef.URI : null;
399
400                         string objectUri;
401                         IMessageSink sink = GetClientChannelSinkChain (url, channelData, out objectUri);
402
403                         if (objectUri == null) objectUri = objRef.URI;
404
405                         lock (uri_hash)
406                         {
407                                 ClientIdentity identity = uri_hash [objRef.URI] as ClientIdentity;
408                                 if (identity != null) 
409                                         return identity;        // Object already registered
410
411                                 // Creates an identity and a proxy for the remote object
412
413                                 identity = new ClientIdentity (objectUri, objRef);
414                                 identity.ChannelSink = sink;
415
416                                 if (proxyType != null)
417                                 {
418                                         RemotingProxy proxy = new RemotingProxy (proxyType, identity);
419                                         identity.ClientProxy = (MarshalByRefObject) proxy.GetTransparentProxy();
420                                 }
421
422                                 // Registers the identity
423                                 uri_hash [objRef.URI] = identity;
424                                 return identity;
425                         }
426                 }
427
428                 static IMessageSink GetClientChannelSinkChain(string url, object channelData, out string objectUri)
429                 {
430                         IMessageSink sink = ChannelServices.CreateClientChannelSinkChain (url, channelData, out objectUri);
431                         if (sink == null) 
432                         {
433                                 if (url != null) 
434                                 {
435                                         string msg = String.Format ("Cannot create channel sink to connect to URL {0}. An appropriate channel has probably not been registered.", url); 
436                                         throw new RemotingException (msg);
437                                 }
438                                 else 
439                                 {
440                                         string msg = String.Format ("Cannot create channel sink to connect to the remote object. An appropriate channel has probably not been registered.", url); 
441                                         throw new RemotingException (msg);
442                                 }
443                         }
444                         return sink;
445                 }
446
447                 internal static ClientActivatedIdentity CreateContextBoundObjectIdentity(Type objectType)
448                 {
449                         ClientActivatedIdentity identity = new ClientActivatedIdentity (null, objectType);
450                         identity.ChannelSink = ChannelServices.CrossContextChannel;
451                         return identity;
452                 }
453
454                 internal static ClientActivatedIdentity CreateClientActivatedServerIdentity(MarshalByRefObject realObject, Type objectType, string objectUri)
455                 {
456                         ClientActivatedIdentity identity = new ClientActivatedIdentity (objectUri, objectType);
457                         identity.AttachServerObject (realObject, Context.DefaultContext);
458                         RegisterServerIdentity (identity);
459                         identity.StartTrackingLifetime ((ILease)realObject.InitializeLifetimeService ());
460                         return identity;
461                 }
462
463                 internal static ServerIdentity CreateWellKnownServerIdentity(Type objectType, string objectUri, WellKnownObjectMode mode)
464                 {
465                         ServerIdentity identity;
466
467                         if (mode == WellKnownObjectMode.SingleCall)
468                                 identity = new  SingleCallIdentity(objectUri, Context.DefaultContext, objectType);
469                         else
470                                 identity = new  SingletonIdentity(objectUri, Context.DefaultContext, objectType);
471
472                         RegisterServerIdentity (identity);
473                         return identity;
474                 }
475
476                 private static void RegisterServerIdentity(ServerIdentity identity)
477                 {
478                         lock (uri_hash)
479                         {
480                                 if (uri_hash.ContainsKey (identity.ObjectUri)) 
481                                         throw new RemotingException ("Uri already in use: " + identity.ObjectUri);
482
483                                 uri_hash[identity.ObjectUri] = identity;
484                         }
485                 }
486
487                 internal static object GetProxyForRemoteObject (ObjRef objref, Type classToProxy)
488                 {
489                         ClientActivatedIdentity identity = GetIdentityForUri (objref.URI) as ClientActivatedIdentity;
490                         if (identity != null) return identity.GetServerObject ();
491                         else return GetRemoteObject (objref, classToProxy);
492                 }
493
494                 internal static object GetRemoteObject(ObjRef objRef, Type proxyType)
495                 {
496                         ClientIdentity id = GetOrCreateClientIdentity (objRef, proxyType);
497                         return id.ClientProxy;
498                 }
499
500                 internal static object GetDomainProxy(AppDomain domain) 
501                 {
502                         byte[] data = null;
503
504                         Context currentContext = Thread.CurrentContext;
505                         AppDomain currentDomain = AppDomain.InternalSetDomain (domain);
506                         try 
507                         {
508                                 data = domain.GetMarshalledDomainObjRef ();
509                         }
510                         finally 
511                         {
512                                 AppDomain.InternalSetDomain (currentDomain);
513                                 AppDomain.InternalSetContext (currentContext);
514                         }
515
516                         MemoryStream stream = new MemoryStream (data);
517                         ObjRef appref = (ObjRef) CADSerializer.DeserializeObject (stream);
518                         return (AppDomain) RemotingServices.Unmarshal(appref);
519                 }
520
521                 private static void RegisterInternalChannels() 
522                 {
523                         CrossAppDomainChannel.RegisterCrossAppDomainChannel();
524                 }
525                 
526                 internal static void DisposeIdentity (ServerIdentity ident)
527                 {
528                         lock (uri_hash)
529                         {
530                                 uri_hash.Remove (ident.ObjectUri);
531                         }
532                 }
533
534                 internal static Identity GetMessageTargetIdentity (IMessage msg)
535                 {
536                         // Returns the identity where the message is sent
537
538                         if (msg is IInternalMessage) 
539                                 return ((IInternalMessage)msg).TargetIdentity;
540
541                         lock (uri_hash)
542                         {
543                                 return uri_hash [((IMethodMessage)msg).Uri] as ServerIdentity;
544                         }
545                 }
546
547                 internal static void SetMessageTargetIdentity (IMessage msg, Identity ident)
548                 {
549                         if (msg is IInternalMessage) 
550                                 ((IInternalMessage)msg).TargetIdentity = ident;
551                 }
552
553                 #endregion
554         }
555 }