2003-02-07 Patrik Torstensson
[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
25 namespace System.Runtime.Remoting
26 {
27         public sealed class RemotingServices 
28         {
29                 // Holds the identities of the objects, using uri as index
30                 static Hashtable uri_hash = new Hashtable ();           
31
32                 internal static string app_id;
33                 static int next_id = 1;
34                 
35                 static RemotingServices ()
36                 {
37                         app_id = "/" + Guid.NewGuid().ToString().Replace('-', '_') + "/";
38
39                         CreateWellKnownServerIdentity (typeof(RemoteActivator), "RemoteActivationService.rem", WellKnownObjectMode.Singleton);
40                 }
41         
42                 private RemotingServices () {}
43
44                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
45                 internal extern static object InternalExecute (MonoMethod method, Object obj,
46                                                                Object[] parameters, out object [] out_args);
47
48                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
49                 public extern static bool IsTransparentProxy (object proxy);
50                 
51                 internal static IMethodReturnMessage InternalExecuteMessage (
52                         MarshalByRefObject target, IMethodCallMessage reqMsg)
53                 {
54                         ReturnMessage result;
55                         
56                         MonoMethod method = (MonoMethod)reqMsg.MethodBase;
57
58                         try {
59                                 object [] out_args;
60                                 object rval = InternalExecute (method, target, reqMsg.Args, out out_args);
61                                 result = new ReturnMessage (rval, out_args, out_args.Length,
62                                                             reqMsg.LogicalCallContext, reqMsg);
63                         
64                         } catch (Exception e) {
65                                 result = new ReturnMessage (e, reqMsg);
66                         }
67
68                         return result;
69                 }
70
71                 public static IMethodReturnMessage ExecuteMessage (
72                         MarshalByRefObject target, IMethodCallMessage reqMsg)
73                 {
74                         if (IsTransparentProxy(target))
75                         {
76                                 // Message must go through all chain of sinks
77                                 RealProxy rp = GetRealProxy (target);
78                                 return (IMethodReturnMessage) rp.Invoke (reqMsg);
79                         }
80                         else    // Direct call
81                                 return InternalExecuteMessage (target, reqMsg);
82                 }
83
84                 public static object Connect (Type classToProxy, string url)
85                 {
86                         ObjRef objRef = new ObjRef (classToProxy, url, null);
87                         return GetRemoteObject(objRef);
88                 }
89
90                 public static object Connect (Type classToProxy, string url, object data)
91                 {
92                         ObjRef objRef = new ObjRef (classToProxy, url, data);
93                         return GetRemoteObject(objRef);
94                 }
95
96                 public static Type GetServerTypeForUri (string uri)
97                 {
98                         Identity ident = GetIdentityForUri (uri);
99                         if (ident == null) return null;
100                         return ident.ObjectType;
101                 }
102
103                 public static string GetObjectUri (MarshalByRefObject obj)
104                 {
105                         Identity ident = GetObjectIdentity(obj);
106                         if (ident != null) return ident.ObjectUri;
107                         else return null;
108                 }
109
110                 public static object Unmarshal (ObjRef objref)
111                 {
112                         return Unmarshal(objref, false);
113                 }
114
115                 public static object Unmarshal (ObjRef objref, bool fRefine)
116                 {
117                         // FIXME: use type name when fRefine==true
118
119                         if (objref.IsReferenceToWellKnow)
120                                 return GetRemoteObject(objref);
121                         else
122                         {
123                                 ClientActivatedIdentity identity = uri_hash [objref.URI] as ClientActivatedIdentity;
124                                 if (identity != null) return identity.GetServerObject ();
125                                 else return GetRemoteObject (objref);
126                         }
127                 }
128
129                 public static ObjRef Marshal (MarshalByRefObject obj)
130                 {
131                         return Marshal (obj, null, null);
132                 }
133                 
134                 public static ObjRef Marshal (MarshalByRefObject obj, string uri)
135                 {
136                         return Marshal (obj, uri, null);
137                 }
138                 
139                 public static ObjRef Marshal (MarshalByRefObject obj, string uri, Type requested_type)
140                 {
141                         if (IsTransparentProxy (obj))
142                         {
143                                 RealProxy proxy = RemotingServices.GetRealProxy(obj);
144                                 if (proxy != null && proxy.ObjectIdentity != null)
145                                 {
146                                         if (uri != null)
147                                                 throw new RemotingException ("It is not possible marshal a proxy of a remote object");
148
149                                         Console.WriteLine("Marshal() TP RealProxy -> uri = " + proxy.ObjectIdentity.ObjectUri);
150                                         
151                                         ObjRef o =  proxy.ObjectIdentity.CreateObjRef(requested_type);
152
153                                         Console.WriteLine("Marshal() channel data = " + o.ChannelInfo);
154                                         
155                                         return o;
156                                 }
157                         }
158
159                         if (requested_type == null) requested_type = obj.GetType();
160
161                         if (uri == null) 
162                         {
163                                 uri = app_id + Environment.TickCount + "_" + next_id++;
164                                 CreateClientActivatedServerIdentity (obj, requested_type, uri);
165                         }
166                         else
167                         {
168                                 ClientActivatedIdentity identity = uri_hash [uri] as ClientActivatedIdentity;
169                                 if (identity == null || obj != identity.GetServerObject()) 
170                                         CreateClientActivatedServerIdentity (obj, requested_type, uri);
171                         }
172
173                         return obj.CreateObjRef(requested_type);
174                 }
175
176                 public static RealProxy GetRealProxy (object proxy)
177                 {
178                         if (!IsTransparentProxy(proxy)) throw new RemotingException("Cannot get the real proxy from an object that is not a transparent proxy");
179                         return (RealProxy)((TransparentProxy)proxy)._rp;
180                 }
181
182                 public static MethodBase GetMethodBaseFromMethodMessage(IMethodMessage msg)
183                 {
184                         Type type = Type.GetType (msg.TypeName);
185                         if (type == null)
186                                 throw new RemotingException ("Type '" + msg.TypeName + "' not found!");
187
188                         BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
189                         if (msg.MethodSignature == null)
190                                 return type.GetMethod (msg.MethodName, bflags);
191                         else
192                                 return type.GetMethod (msg.MethodName, bflags, null, (Type[]) msg.MethodSignature, null);
193                 }
194
195                 public static void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
196                 {
197                         if (obj == null) throw new ArgumentNullException ("obj");
198
199                         ObjRef oref = Marshal ((MarshalByRefObject)obj);
200                         oref.GetObjectData (info, context);
201                 }
202
203                 public static ObjRef GetObjRefForProxy(MarshalByRefObject obj)
204                 {
205                         Identity ident = GetObjectIdentity(obj);
206                         if (ident == null) return null;
207                         else return ident.CreateObjRef(null);
208                 }
209
210                 [MonoTODO]
211                 public static string GetSessionIdForMethodMessage(IMethodMessage msg)
212                 {
213                         throw new NotImplementedException (); 
214                 }
215
216                 public static bool IsMethodOverloaded(IMethodMessage msg)
217                 {
218                         Type type = msg.MethodBase.DeclaringType;
219                         MemberInfo[] members = type.GetMember (msg.MethodName, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
220                         return members.Length > 1;
221                 }
222
223                 public static bool IsObjectOutOfAppDomain(object tp)
224                 {
225                         Identity ident = GetObjectIdentity((MarshalByRefObject)tp);
226                         if (ident != null) return !ident.IsFromThisAppDomain;
227                         else return false;
228                 }
229
230                 public static bool IsObjectOutOfContext(object tp)
231                 {
232                         ServerIdentity ident = GetObjectIdentity((MarshalByRefObject)tp) as ServerIdentity;
233                         if (ident != null) return ident.Context != System.Threading.Thread.CurrentContext;
234                         else return false;
235                 }
236
237                 public static bool IsOneWay(MethodBase method)
238                 {
239                         object[] atts = method.GetCustomAttributes (typeof (OneWayAttribute), false);
240                         return atts.Length > 0;
241                 }
242
243                 public static void SetObjectUriForMarshal(MarshalByRefObject obj, string uri)
244                 {
245                         if (IsTransparentProxy (obj)) throw new RemotingException ("SetObjectUriForMarshal method should only be called for MarshalByRefObjects that exist in the current AppDomain.");
246                         Marshal (obj, uri);
247                 }
248
249                 #region Internal Methods
250                 
251                 internal static Identity GetIdentityForUri (string uri)
252                 {
253                         lock (uri_hash)
254                         {
255                                 return (Identity)uri_hash [uri];
256                         }
257                 }
258
259                 internal static Identity GetObjectIdentity (MarshalByRefObject obj)
260                 {
261                         if (IsTransparentProxy(obj))
262                                 return GetRealProxy (obj).ObjectIdentity;
263                         else
264                                 return obj.ObjectIdentity;
265                 }
266
267                 private static ClientIdentity GetOrCreateClientIdentity(ObjRef objRef)
268                 {
269                         // This method looks for an identity for the given url. 
270                         // If an identity is not found, it creates the identity and 
271                         // assigns it a proxy to the remote object.
272
273                         // Creates the client sink chain for the given url or channelData.
274                         // It will also get the object uri from the url.
275
276                         object channelData = objRef.ChannelInfo != null ? objRef.ChannelInfo.ChannelData : null;
277                         string url = (channelData == null) ? objRef.URI : null;
278
279                         string objectUri;
280                         IMessageSink sink = ChannelServices.CreateClientChannelSinkChain (url, channelData, out objectUri);
281                         if (sink == null) 
282                         {
283                                 if (url != null) {
284                                         string msg = String.Format ("Cannot create channel sink to connect to URL {0}. An appropriate channel has probably not been registered.", url); 
285                                         throw new RemotingException (msg);
286                                 }
287                                 else {
288                                         string msg = String.Format ("Cannot create channel sink to connect to the remote object. An appropriate channel has probably not been registered.", url); 
289                                         throw new RemotingException (msg);
290                                 }
291                         }
292
293                         if (objectUri == null) objectUri = objRef.URI;
294
295                         lock (uri_hash)
296                         {
297                                 ClientIdentity identity = uri_hash [objRef.URI] as ClientIdentity;
298                                 if (identity != null) 
299                                         return identity;        // Object already registered
300
301                                 // Creates an identity and a proxy for the remote object
302
303                                 identity = new ClientIdentity (objectUri, objRef);
304                                 identity.ClientSink = sink;
305
306                                 RemotingProxy proxy = new RemotingProxy (Type.GetType (objRef.TypeInfo.TypeName), identity);
307                                 identity.ClientProxy = (MarshalByRefObject) proxy.GetTransparentProxy();
308
309                                 // Registers the identity
310                                 uri_hash [objRef.URI] = identity;
311                                 return identity;
312                         }
313                 }
314
315                 internal static ClientActivatedIdentity CreateClientActivatedServerIdentity(MarshalByRefObject realObject, Type objectType, string objectUri)
316                 {
317                         ClientActivatedIdentity identity = new ClientActivatedIdentity (objectUri, Context.DefaultContext, objectType);
318                         identity.AttachServerObject (realObject);
319                         RegisterServerIdentity (identity);
320                         return identity;
321                 }
322
323                 internal static ServerIdentity CreateWellKnownServerIdentity(Type objectType, string objectUri, WellKnownObjectMode mode)
324                 {
325                         ServerIdentity identity;
326
327                         if (mode == WellKnownObjectMode.SingleCall)
328                                 identity = new  SingleCallIdentity(objectUri, Context.DefaultContext, objectType);
329                         else
330                                 identity = new  SingletonIdentity(objectUri, Context.DefaultContext, objectType);
331
332                         RegisterServerIdentity (identity);
333                         return identity;
334                 }
335
336                 private static void RegisterServerIdentity(Identity identity)
337                 {
338                         lock (uri_hash)
339                         {
340                                 if (uri_hash.ContainsKey (identity.ObjectUri)) 
341                                         throw new RemotingException ("Uri already in use: " + identity.ObjectUri);
342
343                                 uri_hash[identity.ObjectUri] = identity;
344                         }
345                 }
346
347                 internal static object GetRemoteObject(ObjRef objRef)
348                 {
349                         ClientIdentity id = GetOrCreateClientIdentity (objRef);
350                         return id.ClientProxy;
351                 }
352
353                 internal static object GetDomainProxy(AppDomain domain) 
354                 {
355                         ObjRef appRef = null;
356
357                         // Make sure that the channels is active in this domain
358                         RegisterInternalChannels();
359
360                         // this should use contexts in the future
361                         AppDomain currentDomain = AppDomain.InternalSetDomain (domain);
362                         try 
363                         {
364                                 // Make sure that our new domain also has the internal channels
365                                 RegisterInternalChannels();
366
367                                 appRef = RemotingServices.Marshal(domain, null, null);
368                         }
369                         finally 
370                         {
371                                 AppDomain.InternalSetDomain (currentDomain);
372                         }
373
374                         return (AppDomain) RemotingServices.Unmarshal(appRef);
375                 }
376
377                 private static void RegisterInternalChannels() 
378                 {
379                         CrossAppDomainChannel.RegisterCrossAppDomainChannel();
380                 }
381                 
382                 internal static void DisposeIdentity (Identity ident)
383                 {
384                         uri_hash.Remove (ident.ObjectUri);
385                 }
386
387                 internal static ServerIdentity GetMessageTargetIdentity (IMessage msg)
388                 {
389                         // Returns the identity where the message is sent
390                         // TODO: check for identity embedded in MethodCall
391
392                         lock (uri_hash)
393                         {
394                                 return uri_hash [((IMethodMessage)msg).Uri] as ServerIdentity;
395                         }
396                 }
397
398                 #endregion
399         }
400 }