2003-03-15 Lluis Sanchez Gual <lluis@ideary.com>
[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)reqMsg.MethodBase;
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);
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);
95                 }
96
97                 public static bool Disconnect (MarshalByRefObject obj)
98                 {
99                         if (obj == null) throw new ArgumentNullException ("obj");
100                         if (IsTransparentProxy (obj)) throw new ArgumentException ("The obj parameter is a proxy");
101
102                         ServerIdentity identity = obj.ObjectIdentity;
103                         if (identity == null || !identity.IsConnected)
104                                 return false;
105                         else
106                         {
107                                 LifetimeServices.StopTrackingLifetime (identity);
108                                 DisposeIdentity (identity);
109                                 return true;
110                         }
111                 }
112
113                 public static Type GetServerTypeForUri (string uri)
114                 {
115                         Identity ident = GetIdentityForUri (uri);
116                         if (ident == null) return null;
117                         return ident.ObjectType;
118                 }
119
120                 public static string GetObjectUri (MarshalByRefObject obj)
121                 {
122                         Identity ident = GetObjectIdentity(obj);
123                         if (ident != null) return ident.ObjectUri;
124                         else return null;
125                 }
126
127                 public static object Unmarshal (ObjRef objref)
128                 {
129                         return Unmarshal(objref, false);
130                 }
131
132                 public static object Unmarshal (ObjRef objref, bool fRefine)
133                 {
134                         // FIXME: use type name when fRefine==true
135
136                         if (objref.IsReferenceToWellKnow)
137                                 return GetRemoteObject(objref);
138                         else
139                         {
140                                 if (objref.ServerType.IsContextful)
141                                 {
142                                         // Look for a ProxyAttribute
143                                         ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (objref.ServerType, typeof(ProxyAttribute),true);
144                                         if (att != null)
145                                                 return att.CreateProxy (objref, objref.ServerType, null, null).GetTransparentProxy();
146                                 }
147                                 return GetProxyForRemoteObject (objref, fRefine);
148                         }
149                 }
150
151                 public static ObjRef Marshal (MarshalByRefObject obj)
152                 {
153                         return Marshal (obj, null, null);
154                 }
155                 
156                 public static ObjRef Marshal (MarshalByRefObject obj, string uri)
157                 {
158                         return Marshal (obj, uri, null);
159                 }
160                 
161                 public static ObjRef Marshal (MarshalByRefObject obj, string uri, Type requested_type)
162                 {
163                         if (IsTransparentProxy (obj))
164                         {
165                                 RealProxy proxy = RemotingServices.GetRealProxy(obj);
166                                 Identity identity = proxy.ObjectIdentity;
167
168                                 if (identity != null)
169                                 {
170                                         if (identity.ObjectType.IsContextful && !identity.IsConnected)
171                                         {
172                                                 // Unregistered local contextbound object. Register now.
173                                                 ClientActivatedIdentity cboundIdentity = (ClientActivatedIdentity)identity;
174                                                 if (uri == null) uri = NewUri();
175                                                 cboundIdentity.ObjectUri = uri;
176                                                 RegisterServerIdentity (cboundIdentity);
177                                                 cboundIdentity.StartTrackingLifetime ((ILease)obj.InitializeLifetimeService());
178                                                 return cboundIdentity.CreateObjRef(requested_type);
179                                         }
180                                         else if (uri != null)
181                                                 throw new RemotingException ("It is not possible marshal a proxy of a remote object");
182
183                                         return proxy.ObjectIdentity.CreateObjRef(requested_type);
184                                 }
185                         }
186
187                         if (requested_type == null) requested_type = obj.GetType();
188
189                         if (uri == null) 
190                         {
191                                 uri = NewUri();
192                                 CreateClientActivatedServerIdentity (obj, requested_type, uri);
193                         }
194                         else
195                         {
196                                 ClientActivatedIdentity identity = uri_hash [uri] as ClientActivatedIdentity;
197                                 if (identity == null || obj != identity.GetServerObject()) 
198                                         CreateClientActivatedServerIdentity (obj, requested_type, uri);
199                         }
200
201                         return obj.CreateObjRef(requested_type);
202                 }
203
204                 static string NewUri ()
205                 {
206                         return app_id + Environment.TickCount + "_" + next_id++;
207                 }
208
209                 public static RealProxy GetRealProxy (object proxy)
210                 {
211                         if (!IsTransparentProxy(proxy)) throw new RemotingException("Cannot get the real proxy from an object that is not a transparent proxy");
212                         return (RealProxy)((TransparentProxy)proxy)._rp;
213                 }
214
215                 public static MethodBase GetMethodBaseFromMethodMessage(IMethodMessage msg)
216                 {
217                         Type type = Type.GetType (msg.TypeName);
218                         if (type == null)
219                                 throw new RemotingException ("Type '" + msg.TypeName + "' not found!");
220
221                         BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
222                         if (msg.MethodSignature == null)
223                                 return type.GetMethod (msg.MethodName, bflags);
224                         else
225                                 return type.GetMethod (msg.MethodName, bflags, null, (Type[]) msg.MethodSignature, null);
226                 }
227
228                 public static void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
229                 {
230                         if (obj == null) throw new ArgumentNullException ("obj");
231
232                         ObjRef oref = Marshal ((MarshalByRefObject)obj);
233                         oref.GetObjectData (info, context);
234                 }
235
236                 public static ObjRef GetObjRefForProxy(MarshalByRefObject obj)
237                 {
238                         Identity ident = GetObjectIdentity(obj);
239                         if (ident == null) return null;
240                         else return ident.CreateObjRef(null);
241                 }
242
243                 [MonoTODO]
244                 public static string GetSessionIdForMethodMessage(IMethodMessage msg)
245                 {
246                         throw new NotImplementedException (); 
247                 }
248
249                 public static bool IsMethodOverloaded(IMethodMessage msg)
250                 {
251                         Type type = msg.MethodBase.DeclaringType;
252                         MemberInfo[] members = type.GetMember (msg.MethodName, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
253                         return members.Length > 1;
254                 }
255
256                 public static bool IsObjectOutOfAppDomain(object tp)
257                 {
258                         Identity ident = GetObjectIdentity((MarshalByRefObject)tp);
259                         if (ident != null) return !ident.IsFromThisAppDomain;
260                         else return false;
261                 }
262
263                 public static bool IsObjectOutOfContext(object tp)
264                 {
265                         ServerIdentity ident = GetObjectIdentity((MarshalByRefObject)tp) as ServerIdentity;
266                         if (ident != null) return ident.Context != System.Threading.Thread.CurrentContext;
267                         else return false;
268                 }
269
270                 public static bool IsOneWay(MethodBase method)
271                 {
272                         object[] atts = method.GetCustomAttributes (typeof (OneWayAttribute), false);
273                         return atts.Length > 0;
274                 }
275
276                 public static void SetObjectUriForMarshal(MarshalByRefObject obj, string uri)
277                 {
278                         if (IsTransparentProxy (obj)) throw new RemotingException ("SetObjectUriForMarshal method should only be called for MarshalByRefObjects that exist in the current AppDomain.");
279                         Marshal (obj, uri);
280                 }
281
282                 #region Internal Methods
283                 
284                 internal static object CreateClientProxy (ActivatedClientTypeEntry entry, object[] activationAttributes)
285                 {
286                         if (entry.ContextAttributes != null || activationAttributes != null)
287                         {
288                                 ArrayList props = new ArrayList ();
289                                 if (entry.ContextAttributes != null) props.AddRange (entry.ContextAttributes);
290                                 if (activationAttributes != null) props.AddRange (activationAttributes);
291                                 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, props.ToArray ());
292                         }
293                         else
294                                 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, null);
295                 }
296         
297                 internal static object CreateClientProxy (Type objectType, string url, object[] activationAttributes)
298                 {
299                         string activationUrl = url + "/RemoteActivationService.rem";
300
301                         string objectUri;
302                         IMessageSink sink = GetClientChannelSinkChain (activationUrl, null, out objectUri);
303
304                         RemotingProxy proxy = new RemotingProxy (objectType, activationUrl, activationAttributes);
305                         return proxy.GetTransparentProxy();
306                 }
307         
308                 internal static object CreateClientProxy (WellKnownClientTypeEntry entry)
309                 {
310                         return Connect (entry.ObjectType, entry.ObjectUrl, null);
311                 }
312         
313                 internal static object CreateClientProxyForContextBound (Type type, object[] activationAttributes)
314                 {
315                         if (type.IsContextful)
316                         {
317                                 // Look for a ProxyAttribute
318                                 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (type, typeof(ProxyAttribute), true);
319                                 if (att != null)
320                                         return att.CreateInstance (type);
321                         }
322                         RemotingProxy proxy = new RemotingProxy (type, ChannelServices.CrossContextUrl, activationAttributes);
323                         return proxy.GetTransparentProxy();
324                 }
325         
326                 internal static Identity GetIdentityForUri (string uri)
327                 {
328                         lock (uri_hash)
329                         {
330                                 return (Identity)uri_hash [uri];
331                         }
332                 }
333
334                 internal static Identity GetObjectIdentity (MarshalByRefObject obj)
335                 {
336                         if (IsTransparentProxy(obj))
337                                 return GetRealProxy (obj).ObjectIdentity;
338                         else
339                                 return obj.ObjectIdentity;
340                 }
341
342                 internal static ClientIdentity GetOrCreateClientIdentity(ObjRef objRef, RealProxy proxyToAttach)
343                 {
344                         // This method looks for an identity for the given url. 
345                         // If an identity is not found, it creates the identity and 
346                         // assigns it a proxy to the remote object.
347
348                         // Creates the client sink chain for the given url or channelData.
349                         // It will also get the object uri from the url.
350
351                         object channelData = objRef.ChannelInfo != null ? objRef.ChannelInfo.ChannelData : null;
352                         string url = (channelData == null) ? objRef.URI : null;
353
354                         string objectUri;
355                         IMessageSink sink = GetClientChannelSinkChain (url, channelData, out objectUri);
356
357                         if (objectUri == null) objectUri = objRef.URI;
358
359                         lock (uri_hash)
360                         {
361                                 ClientIdentity identity = uri_hash [objRef.URI] as ClientIdentity;
362                                 if (identity != null) 
363                                         return identity;        // Object already registered
364
365                                 // Creates an identity and a proxy for the remote object
366
367                                 identity = new ClientIdentity (objectUri, objRef);
368                                 identity.ChannelSink = sink;
369
370                                 if (proxyToAttach == null) proxyToAttach = new RemotingProxy (objRef.ServerType, identity);
371                                 identity.ClientProxy = (MarshalByRefObject) proxyToAttach.GetTransparentProxy();
372
373                                 // Registers the identity
374                                 uri_hash [objRef.URI] = identity;
375                                 return identity;
376                         }
377                 }
378
379                 static IMessageSink GetClientChannelSinkChain(string url, object channelData, out string objectUri)
380                 {
381                         IMessageSink sink = ChannelServices.CreateClientChannelSinkChain (url, channelData, out objectUri);
382                         if (sink == null) 
383                         {
384                                 if (url != null) \r
385                                 {
386                                         string msg = String.Format ("Cannot create channel sink to connect to URL {0}. An appropriate channel has probably not been registered.", url); 
387                                         throw new RemotingException (msg);
388                                 }
389                                 else \r
390                                 {
391                                         string msg = String.Format ("Cannot create channel sink to connect to the remote object. An appropriate channel has probably not been registered.", url); 
392                                         throw new RemotingException (msg);
393                                 }
394                         }
395                         return sink;
396                 }
397
398                 internal static ClientActivatedIdentity CreateContextBoundObjectIdentity(Type objectType)
399                 {
400                         ClientActivatedIdentity identity = new ClientActivatedIdentity (null, objectType);
401                         identity.ChannelSink = ChannelServices.CrossContextChannel;
402                         return identity;
403                 }
404
405                 internal static ClientActivatedIdentity CreateClientActivatedServerIdentity(MarshalByRefObject realObject, Type objectType, string objectUri)
406                 {
407                         ClientActivatedIdentity identity = new ClientActivatedIdentity (objectUri, objectType);
408                         identity.AttachServerObject (realObject, Context.DefaultContext);
409                         RegisterServerIdentity (identity);
410                         identity.StartTrackingLifetime ((ILease)realObject.InitializeLifetimeService ());
411                         return identity;
412                 }
413
414                 internal static ServerIdentity CreateWellKnownServerIdentity(Type objectType, string objectUri, WellKnownObjectMode mode)
415                 {
416                         ServerIdentity identity;
417
418                         if (mode == WellKnownObjectMode.SingleCall)
419                                 identity = new  SingleCallIdentity(objectUri, Context.DefaultContext, objectType);
420                         else
421                                 identity = new  SingletonIdentity(objectUri, Context.DefaultContext, objectType);
422
423                         RegisterServerIdentity (identity);
424                         return identity;
425                 }
426
427                 private static void RegisterServerIdentity(ServerIdentity identity)
428                 {
429                         lock (uri_hash)
430                         {
431                                 if (uri_hash.ContainsKey (identity.ObjectUri)) 
432                                         throw new RemotingException ("Uri already in use: " + identity.ObjectUri);
433
434                                 uri_hash[identity.ObjectUri] = identity;
435                         }
436                 }
437
438                 internal static object GetProxyForRemoteObject (ObjRef objref, bool fRefine)
439                 {
440                         ClientActivatedIdentity identity = uri_hash [objref.URI] as ClientActivatedIdentity;
441                         if (identity != null) return identity.GetServerObject ();
442                         else return GetRemoteObject (objref);
443                 }
444
445                 internal static object GetRemoteObject(ObjRef objRef)
446                 {
447                         ClientIdentity id = GetOrCreateClientIdentity (objRef, null);
448                         return id.ClientProxy;
449                 }
450
451                 internal static object GetDomainProxy(AppDomain domain) 
452                 {
453                         byte[] data = null;
454
455                         Context currentContext = Thread.CurrentContext;
456                         AppDomain currentDomain = AppDomain.InternalSetDomain (domain);
457                         try 
458                         {
459                                 data = domain.GetMarshalledDomainObjRef ();
460                         }
461                         finally 
462                         {
463                                 AppDomain.InternalSetDomain (currentDomain);
464                                 AppDomain.InternalSetContext (currentContext);
465                         }
466
467                         MemoryStream stream = new MemoryStream (data);
468                         ObjRef appref = (ObjRef) CADSerializer.DeserializeObject (stream);
469                         return (AppDomain) RemotingServices.Unmarshal(appref);
470                 }
471
472                 private static void RegisterInternalChannels() 
473                 {
474                         CrossAppDomainChannel.RegisterCrossAppDomainChannel();
475                 }
476                 
477                 internal static void DisposeIdentity (ServerIdentity ident)
478                 {
479                         uri_hash.Remove (ident.ObjectUri);
480                 }
481
482                 internal static Identity GetMessageTargetIdentity (IMessage msg)
483                 {
484                         // Returns the identity where the message is sent
485
486                         if (msg is IInternalMessage) 
487                                 return ((IInternalMessage)msg).TargetIdentity;
488
489                         lock (uri_hash)
490                         {
491                                 return uri_hash [((IMethodMessage)msg).Uri] as ServerIdentity;
492                         }
493                 }
494
495                 internal static void SetMessageTargetIdentity (IMessage msg, Identity ident)
496                 {
497                         if (msg is IInternalMessage) 
498                                 ((IInternalMessage)msg).TargetIdentity = ident;
499                 }
500
501                 #endregion
502         }
503 }