b3dbef05a623636e84547c3790a1906d4ebfa094
[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 = uri_hash [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                 [MonoTODO]
262                 public static string GetSessionIdForMethodMessage(IMethodMessage msg)
263                 {
264                         throw new NotImplementedException (); 
265                 }
266
267                 public static bool IsMethodOverloaded(IMethodMessage msg)
268                 {
269                         Type type = msg.MethodBase.DeclaringType;
270                         MemberInfo[] members = type.GetMember (msg.MethodName, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
271                         return members.Length > 1;
272                 }
273
274                 public static bool IsObjectOutOfAppDomain(object tp)
275                 {
276                         Identity ident = GetObjectIdentity((MarshalByRefObject)tp);
277                         if (ident != null) return !ident.IsFromThisAppDomain;
278                         else return false;
279                 }
280
281                 public static bool IsObjectOutOfContext(object tp)
282                 {
283                         ServerIdentity ident = GetObjectIdentity((MarshalByRefObject)tp) as ServerIdentity;
284                         if (ident != null) return ident.Context != System.Threading.Thread.CurrentContext;
285                         else return false;
286                 }
287
288                 public static bool IsOneWay(MethodBase method)
289                 {
290                         // TODO: use internal call for better performance
291                         object[] atts = method.GetCustomAttributes (typeof (OneWayAttribute), false);
292                         return atts.Length > 0;
293                 }
294
295                 public static bool IsAsyncMessage(IMessage msg)
296                 {
297                         if (! (msg is MonoMethodMessage)) return false;
298                         else if (((MonoMethodMessage)msg).IsAsync) return true;
299                         else if (IsOneWay (((MonoMethodMessage)msg).MethodBase)) return true;
300                         else return false;
301                 }
302
303                 public static void SetObjectUriForMarshal(MarshalByRefObject obj, string uri)
304                 {
305                         if (IsTransparentProxy (obj)) throw new RemotingException ("SetObjectUriForMarshal method should only be called for MarshalByRefObjects that exist in the current AppDomain.");
306                         Marshal (obj, uri);
307                 }
308
309                 #region Internal Methods
310                 
311                 internal static object CreateClientProxy (ActivatedClientTypeEntry entry, object[] activationAttributes)
312                 {
313                         if (entry.ContextAttributes != null || activationAttributes != null)
314                         {
315                                 ArrayList props = new ArrayList ();
316                                 if (entry.ContextAttributes != null) props.AddRange (entry.ContextAttributes);
317                                 if (activationAttributes != null) props.AddRange (activationAttributes);
318                                 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, props.ToArray ());
319                         }
320                         else
321                                 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, null);
322                 }
323         
324                 internal static object CreateClientProxy (Type objectType, string url, object[] activationAttributes)
325                 {
326                         string activationUrl = url + "/RemoteActivationService.rem";
327
328                         string objectUri;
329                         IMessageSink sink = GetClientChannelSinkChain (activationUrl, null, out objectUri);
330
331                         RemotingProxy proxy = new RemotingProxy (objectType, activationUrl, activationAttributes);
332                         return proxy.GetTransparentProxy();
333                 }
334         
335                 internal static object CreateClientProxy (WellKnownClientTypeEntry entry)
336                 {
337                         return Connect (entry.ObjectType, entry.ObjectUrl, null);
338                 }
339         
340                 internal static object CreateClientProxyForContextBound (Type type, object[] activationAttributes)
341                 {
342                         if (type.IsContextful)
343                         {
344                                 // Look for a ProxyAttribute
345                                 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (type, typeof(ProxyAttribute), true);
346                                 if (att != null)
347                                         return att.CreateInstance (type);
348                         }
349                         RemotingProxy proxy = new RemotingProxy (type, ChannelServices.CrossContextUrl, activationAttributes);
350                         return proxy.GetTransparentProxy();
351                 }
352         
353                 internal static Identity GetIdentityForUri (string uri)
354                 {
355                         lock (uri_hash)
356                         {
357                                 return (Identity)uri_hash [uri];
358                         }
359                 }
360
361                 internal static Identity GetObjectIdentity (MarshalByRefObject obj)
362                 {
363                         if (IsTransparentProxy(obj))
364                                 return GetRealProxy (obj).ObjectIdentity;
365                         else
366                                 return obj.ObjectIdentity;
367                 }
368
369                 internal static ClientIdentity GetOrCreateClientIdentity(ObjRef objRef, Type proxyType)
370                 {
371                         // This method looks for an identity for the given url. 
372                         // If an identity is not found, it creates the identity and 
373                         // assigns it a proxy to the remote object.
374
375                         // Creates the client sink chain for the given url or channelData.
376                         // It will also get the object uri from the url.
377
378                         object channelData = objRef.ChannelInfo != null ? objRef.ChannelInfo.ChannelData : null;
379                         string url = (channelData == null) ? objRef.URI : null;
380
381                         string objectUri;
382                         IMessageSink sink = GetClientChannelSinkChain (url, channelData, out objectUri);
383
384                         if (objectUri == null) objectUri = objRef.URI;
385
386                         lock (uri_hash)
387                         {
388                                 ClientIdentity identity = uri_hash [objRef.URI] as ClientIdentity;
389                                 if (identity != null) 
390                                         return identity;        // Object already registered
391
392                                 // Creates an identity and a proxy for the remote object
393
394                                 identity = new ClientIdentity (objectUri, objRef);
395                                 identity.ChannelSink = sink;
396
397                                 if (proxyType != null)
398                                 {
399                                         RemotingProxy proxy = new RemotingProxy (proxyType, identity);
400                                         identity.ClientProxy = (MarshalByRefObject) proxy.GetTransparentProxy();
401                                 }
402
403                                 // Registers the identity
404                                 uri_hash [objRef.URI] = identity;
405                                 return identity;
406                         }
407                 }
408
409                 static IMessageSink GetClientChannelSinkChain(string url, object channelData, out string objectUri)
410                 {
411                         IMessageSink sink = ChannelServices.CreateClientChannelSinkChain (url, channelData, out objectUri);
412                         if (sink == null) 
413                         {
414                                 if (url != null) 
415                                 {
416                                         string msg = String.Format ("Cannot create channel sink to connect to URL {0}. An appropriate channel has probably not been registered.", url); 
417                                         throw new RemotingException (msg);
418                                 }
419                                 else 
420                                 {
421                                         string msg = String.Format ("Cannot create channel sink to connect to the remote object. An appropriate channel has probably not been registered.", url); 
422                                         throw new RemotingException (msg);
423                                 }
424                         }
425                         return sink;
426                 }
427
428                 internal static ClientActivatedIdentity CreateContextBoundObjectIdentity(Type objectType)
429                 {
430                         ClientActivatedIdentity identity = new ClientActivatedIdentity (null, objectType);
431                         identity.ChannelSink = ChannelServices.CrossContextChannel;
432                         return identity;
433                 }
434
435                 internal static ClientActivatedIdentity CreateClientActivatedServerIdentity(MarshalByRefObject realObject, Type objectType, string objectUri)
436                 {
437                         ClientActivatedIdentity identity = new ClientActivatedIdentity (objectUri, objectType);
438                         identity.AttachServerObject (realObject, Context.DefaultContext);
439                         RegisterServerIdentity (identity);
440                         identity.StartTrackingLifetime ((ILease)realObject.InitializeLifetimeService ());
441                         return identity;
442                 }
443
444                 internal static ServerIdentity CreateWellKnownServerIdentity(Type objectType, string objectUri, WellKnownObjectMode mode)
445                 {
446                         ServerIdentity identity;
447
448                         if (mode == WellKnownObjectMode.SingleCall)
449                                 identity = new  SingleCallIdentity(objectUri, Context.DefaultContext, objectType);
450                         else
451                                 identity = new  SingletonIdentity(objectUri, Context.DefaultContext, objectType);
452
453                         RegisterServerIdentity (identity);
454                         return identity;
455                 }
456
457                 private static void RegisterServerIdentity(ServerIdentity identity)
458                 {
459                         lock (uri_hash)
460                         {
461                                 if (uri_hash.ContainsKey (identity.ObjectUri)) 
462                                         throw new RemotingException ("Uri already in use: " + identity.ObjectUri);
463
464                                 uri_hash[identity.ObjectUri] = identity;
465                         }
466                 }
467
468                 internal static object GetProxyForRemoteObject (ObjRef objref, Type classToProxy)
469                 {
470                         ClientActivatedIdentity identity = uri_hash [objref.URI] as ClientActivatedIdentity;
471                         if (identity != null) return identity.GetServerObject ();
472                         else return GetRemoteObject (objref, classToProxy);
473                 }
474
475                 internal static object GetRemoteObject(ObjRef objRef, Type proxyType)
476                 {
477                         ClientIdentity id = GetOrCreateClientIdentity (objRef, proxyType);
478                         return id.ClientProxy;
479                 }
480
481                 internal static object GetDomainProxy(AppDomain domain) 
482                 {
483                         byte[] data = null;
484
485                         Context currentContext = Thread.CurrentContext;
486                         AppDomain currentDomain = AppDomain.InternalSetDomain (domain);
487                         try 
488                         {
489                                 data = domain.GetMarshalledDomainObjRef ();
490                         }
491                         finally 
492                         {
493                                 AppDomain.InternalSetDomain (currentDomain);
494                                 AppDomain.InternalSetContext (currentContext);
495                         }
496
497                         MemoryStream stream = new MemoryStream (data);
498                         ObjRef appref = (ObjRef) CADSerializer.DeserializeObject (stream);
499                         return (AppDomain) RemotingServices.Unmarshal(appref);
500                 }
501
502                 private static void RegisterInternalChannels() 
503                 {
504                         CrossAppDomainChannel.RegisterCrossAppDomainChannel();
505                 }
506                 
507                 internal static void DisposeIdentity (ServerIdentity ident)
508                 {
509                         uri_hash.Remove (ident.ObjectUri);
510                 }
511
512                 internal static Identity GetMessageTargetIdentity (IMessage msg)
513                 {
514                         // Returns the identity where the message is sent
515
516                         if (msg is IInternalMessage) 
517                                 return ((IInternalMessage)msg).TargetIdentity;
518
519                         lock (uri_hash)
520                         {
521                                 return uri_hash [((IMethodMessage)msg).Uri] as ServerIdentity;
522                         }
523                 }
524
525                 internal static void SetMessageTargetIdentity (IMessage msg, Identity ident)
526                 {
527                         if (msg is IInternalMessage) 
528                                 ((IInternalMessage)msg).TargetIdentity = ident;
529                 }
530
531                 #endregion
532         }
533 }