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