2 // System.Runtime.Remoting.RemotingServices.cs
5 // Dietmar Maurer (dietmar@ximian.com)
6 // Lluis Sanchez Gual (lluis@ideary.com)
9 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Diagnostics;
38 using System.Reflection;
39 using System.Threading;
40 using System.Collections;
41 using System.Runtime.Remoting.Messaging;
42 using System.Runtime.Remoting.Proxies;
43 using System.Runtime.Remoting.Channels;
44 using System.Runtime.Remoting.Contexts;
45 using System.Runtime.Remoting.Activation;
46 using System.Runtime.Remoting.Lifetime;
47 using System.Runtime.CompilerServices;
48 using System.Runtime.Serialization;
51 namespace System.Runtime.Remoting
53 public sealed class RemotingServices
55 // Holds the identities of the objects, using uri as index
56 static Hashtable uri_hash = new Hashtable ();
58 internal static string app_id;
59 static int next_id = 1;
61 static RemotingServices ()
63 RegisterInternalChannels ();
64 app_id = Guid.NewGuid().ToString().Replace('-', '_') + "/";
65 CreateWellKnownServerIdentity (typeof(RemoteActivator), "RemoteActivationService.rem", WellKnownObjectMode.Singleton);
68 private RemotingServices () {}
70 [MethodImplAttribute(MethodImplOptions.InternalCall)]
71 internal extern static object InternalExecute (MonoMethod method, Object obj,
72 Object[] parameters, out object [] out_args);
74 [MethodImplAttribute(MethodImplOptions.InternalCall)]
75 public extern static bool IsTransparentProxy (object proxy);
77 internal static IMethodReturnMessage InternalExecuteMessage (
78 MarshalByRefObject target, IMethodCallMessage reqMsg)
82 MonoMethod method = (MonoMethod) target.GetType().GetMethod(reqMsg.MethodName, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance, null, (Type[]) reqMsg.MethodSignature, null);
83 object oldContext = CallContext.SetCurrentCallContext (reqMsg.LogicalCallContext);
88 object rval = InternalExecute (method, target, reqMsg.Args, out out_args);
90 // Collect parameters with Out flag from the request message
91 // FIXME: This can be done in the unmanaged side and will be
94 ParameterInfo[] parameters = method.GetParameters();
95 object[] returnArgs = new object [parameters.Length];
99 foreach (ParameterInfo par in parameters)
101 if (par.IsOut && !par.ParameterType.IsByRef)
102 returnArgs [n++] = reqMsg.GetArg (par.Position);
103 else if (par.ParameterType.IsByRef)
104 returnArgs [n++] = out_args [noa++];
106 returnArgs [n++] = null;
109 result = new ReturnMessage (rval, returnArgs, n, CallContext.CreateLogicalCallContext(), reqMsg);
113 result = new ReturnMessage (e, reqMsg);
116 CallContext.RestoreCallContext (oldContext);
120 public static IMethodReturnMessage ExecuteMessage (
121 MarshalByRefObject target, IMethodCallMessage reqMsg)
123 if (IsTransparentProxy(target))
125 // Message must go through all chain of sinks
126 RealProxy rp = GetRealProxy (target);
127 return (IMethodReturnMessage) rp.Invoke (reqMsg);
130 return InternalExecuteMessage (target, reqMsg);
133 public static object Connect (Type classToProxy, string url)
135 ObjRef objRef = new ObjRef (classToProxy, url, null);
136 return GetRemoteObject (objRef, classToProxy);
139 public static object Connect (Type classToProxy, string url, object data)
141 ObjRef objRef = new ObjRef (classToProxy, url, data);
142 return GetRemoteObject (objRef, classToProxy);
145 public static bool Disconnect (MarshalByRefObject obj)
147 if (obj == null) throw new ArgumentNullException ("obj");
149 ServerIdentity identity;
151 if (IsTransparentProxy (obj))
153 // CBOs are always accessed through a proxy, even in the server, so
154 // for server CBOs it is ok to disconnect a proxy
156 RealProxy proxy = GetRealProxy(obj);
157 if (proxy.GetProxiedType().IsContextful && (proxy.ObjectIdentity is ServerIdentity))
158 identity = proxy.ObjectIdentity as ServerIdentity;
160 throw new ArgumentException ("The obj parameter is a proxy.");
163 identity = obj.ObjectIdentity;
164 obj.ObjectIdentity = null;
167 if (identity == null || !identity.IsConnected)
171 LifetimeServices.StopTrackingLifetime (identity);
172 DisposeIdentity (identity);
177 public static Type GetServerTypeForUri (string uri)
179 ServerIdentity ident = GetIdentityForUri (uri) as ServerIdentity;
180 if (ident == null) return null;
181 return ident.ObjectType;
184 public static string GetObjectUri (MarshalByRefObject obj)
186 Identity ident = GetObjectIdentity(obj);
187 if (ident is ClientIdentity) return ((ClientIdentity)ident).TargetUri;
188 else if (ident != null) return ident.ObjectUri;
192 public static object Unmarshal (ObjRef objref)
194 return Unmarshal(objref, true);
197 public static object Unmarshal (ObjRef objref, bool fRefine)
199 Type classToProxy = fRefine ? objref.ServerType : typeof (MarshalByRefObject);
200 if (classToProxy == null) classToProxy = typeof (MarshalByRefObject);
202 if (objref.IsReferenceToWellKnow)
203 return GetRemoteObject(objref, classToProxy);
206 if (classToProxy.IsContextful)
208 // Look for a ProxyAttribute
209 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (classToProxy, typeof(ProxyAttribute),true);
211 return att.CreateProxy (objref, classToProxy, null, null).GetTransparentProxy();
213 return GetProxyForRemoteObject (objref, classToProxy);
217 public static ObjRef Marshal (MarshalByRefObject obj)
219 return Marshal (obj, null, null);
222 public static ObjRef Marshal (MarshalByRefObject obj, string uri)
224 return Marshal (obj, uri, null);
227 public static ObjRef Marshal (MarshalByRefObject obj, string uri, Type requested_type)
229 if (IsTransparentProxy (obj))
231 RealProxy proxy = RemotingServices.GetRealProxy(obj);
232 Identity identity = proxy.ObjectIdentity;
234 if (identity != null)
236 if (proxy.GetProxiedType().IsContextful && !identity.IsConnected)
238 // Unregistered local contextbound object. Register now.
239 ClientActivatedIdentity cboundIdentity = (ClientActivatedIdentity)identity;
240 if (uri == null) uri = NewUri();
241 cboundIdentity.ObjectUri = uri;
242 RegisterServerIdentity (cboundIdentity);
243 cboundIdentity.StartTrackingLifetime ((ILease)obj.InitializeLifetimeService());
244 return cboundIdentity.CreateObjRef(requested_type);
246 else if (uri != null)
247 throw new RemotingException ("It is not possible marshal a proxy of a remote object.");
249 return proxy.ObjectIdentity.CreateObjRef(requested_type);
253 if (requested_type == null) requested_type = obj.GetType();
257 if (obj.ObjectIdentity == null)
260 CreateClientActivatedServerIdentity (obj, requested_type, uri);
265 ClientActivatedIdentity identity = GetIdentityForUri ("/" + uri) as ClientActivatedIdentity;
266 if (identity == null || obj != identity.GetServerObject())
267 CreateClientActivatedServerIdentity (obj, requested_type, uri);
270 if (IsTransparentProxy (obj))
271 return RemotingServices.GetRealProxy(obj).ObjectIdentity.CreateObjRef (requested_type);
273 return obj.CreateObjRef(requested_type);
276 static string NewUri ()
278 int n = Interlocked.Increment (ref next_id);
279 return app_id + Environment.TickCount + "_" + n + ".rem";
282 public static RealProxy GetRealProxy (object proxy)
284 if (!IsTransparentProxy(proxy)) throw new RemotingException("Cannot get the real proxy from an object that is not a transparent proxy.");
285 return (RealProxy)((TransparentProxy)proxy)._rp;
288 public static MethodBase GetMethodBaseFromMethodMessage(IMethodMessage msg)
290 Type type = Type.GetType (msg.TypeName);
292 throw new RemotingException ("Type '" + msg.TypeName + "' not found.");
294 BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
297 if (msg.MethodSignature == null)
298 method = type.GetMethod (msg.MethodName, bflags);
300 method = type.GetMethod (msg.MethodName, bflags, null, (Type[]) msg.MethodSignature, null);
305 if (msg.MethodSignature == null)
306 return type.GetConstructor (bflags, null, Type.EmptyTypes, null);
308 return type.GetConstructor (bflags, null, (Type[]) msg.MethodSignature, null);
311 public static void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
313 if (obj == null) throw new ArgumentNullException ("obj");
315 ObjRef oref = Marshal ((MarshalByRefObject)obj);
316 oref.GetObjectData (info, context);
319 public static ObjRef GetObjRefForProxy(MarshalByRefObject obj)
321 Identity ident = GetObjectIdentity(obj);
322 if (ident == null) return null;
323 else return ident.CreateObjRef(null);
326 public static object GetLifetimeService (MarshalByRefObject obj)
328 if (obj == null) return null;
329 return obj.GetLifetimeService ();
332 public static IMessageSink GetEnvoyChainForProxy (MarshalByRefObject obj)
334 if (IsTransparentProxy(obj))
335 return ((ClientIdentity)GetRealProxy (obj).ObjectIdentity).EnvoySink;
337 throw new ArgumentException ("obj must be a proxy.","obj");
341 [Conditional ("REMOTING_PERF")]
342 public static void LogRemotingStage (int stage)
344 throw new NotImplementedException ();
347 public static string GetSessionIdForMethodMessage(IMethodMessage msg)
349 // It seems that this it what MS returns.
353 public static bool IsMethodOverloaded(IMethodMessage msg)
355 // TODO: use internal call for better performance
356 Type type = msg.MethodBase.DeclaringType;
357 MemberInfo[] members = type.GetMember (msg.MethodName, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
358 return members.Length > 1;
361 public static bool IsObjectOutOfAppDomain(object tp)
363 // TODO: use internal call for better performance
364 Identity ident = GetObjectIdentity((MarshalByRefObject)tp);
365 if (ident != null) return !ident.IsFromThisAppDomain;
369 public static bool IsObjectOutOfContext(object tp)
371 // TODO: use internal call for better performance
372 ServerIdentity ident = GetObjectIdentity((MarshalByRefObject)tp) as ServerIdentity;
373 if (ident != null) return ident.Context != System.Threading.Thread.CurrentContext;
377 public static bool IsOneWay(MethodBase method)
379 // TODO: use internal call for better performance
380 object[] atts = method.GetCustomAttributes (typeof (OneWayAttribute), false);
381 return atts.Length > 0;
384 internal static bool IsAsyncMessage(IMessage msg)
386 if (! (msg is MonoMethodMessage)) return false;
387 else if (((MonoMethodMessage)msg).IsAsync) return true;
388 else if (IsOneWay (((MonoMethodMessage)msg).MethodBase)) return true;
392 public static void SetObjectUriForMarshal(MarshalByRefObject obj, string uri)
394 if (IsTransparentProxy (obj)) throw new RemotingException ("SetObjectUriForMarshal method should only be called for MarshalByRefObjects that exist in the current AppDomain.");
398 #region Internal Methods
400 internal static object CreateClientProxy (ActivatedClientTypeEntry entry, object[] activationAttributes)
402 if (entry.ContextAttributes != null || activationAttributes != null)
404 ArrayList props = new ArrayList ();
405 if (entry.ContextAttributes != null) props.AddRange (entry.ContextAttributes);
406 if (activationAttributes != null) props.AddRange (activationAttributes);
407 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, props.ToArray ());
410 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, null);
413 internal static object CreateClientProxy (Type objectType, string url, object[] activationAttributes)
415 string activationUrl = url + "/RemoteActivationService.rem";
418 IMessageSink sink = GetClientChannelSinkChain (activationUrl, null, out objectUri);
420 RemotingProxy proxy = new RemotingProxy (objectType, activationUrl, activationAttributes);
421 return proxy.GetTransparentProxy();
424 internal static object CreateClientProxy (WellKnownClientTypeEntry entry)
426 return Connect (entry.ObjectType, entry.ObjectUrl, null);
429 internal static object CreateClientProxyForContextBound (Type type, object[] activationAttributes)
431 if (type.IsContextful)
433 // Look for a ProxyAttribute
434 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (type, typeof(ProxyAttribute), true);
436 return att.CreateInstance (type);
438 RemotingProxy proxy = new RemotingProxy (type, ChannelServices.CrossContextUrl, activationAttributes);
439 return proxy.GetTransparentProxy();
442 internal static Identity GetIdentityForUri (string uri)
446 return (Identity)uri_hash [GetNormalizedUri(uri)];
450 internal static Identity GetObjectIdentity (MarshalByRefObject obj)
452 if (IsTransparentProxy(obj))
453 return GetRealProxy (obj).ObjectIdentity;
455 return obj.ObjectIdentity;
458 internal static ClientIdentity GetOrCreateClientIdentity(ObjRef objRef, Type proxyType, out object clientProxy)
460 // This method looks for an identity for the given url.
461 // If an identity is not found, it creates the identity and
462 // assigns it a proxy to the remote object.
464 // Creates the client sink chain for the given url or channelData.
465 // It will also get the object uri from the url.
467 object channelData = objRef.ChannelInfo != null ? objRef.ChannelInfo.ChannelData : null;
468 string url = (channelData == null) ? objRef.URI : null;
471 IMessageSink sink = GetClientChannelSinkChain (url, channelData, out objectUri);
473 if (objectUri == null) objectUri = objRef.URI;
478 string uri = GetNormalizedUri (objRef.URI);
480 ClientIdentity identity = uri_hash [uri] as ClientIdentity;
481 if (identity != null)
483 // Object already registered
484 clientProxy = identity.ClientProxy;
485 if (clientProxy != null) return identity;
487 // The proxy has just been GCed, so its identity cannot
488 // be reused. Just dispose it.
489 DisposeIdentity (identity);
492 // Creates an identity and a proxy for the remote object
494 identity = new ClientIdentity (objectUri, objRef);
495 identity.ChannelSink = sink;
497 // Registers the identity
498 uri_hash [uri] = identity;
500 if (proxyType != null)
502 RemotingProxy proxy = new RemotingProxy (proxyType, identity);
503 clientProxy = proxy.GetTransparentProxy();
504 identity.ClientProxy = (MarshalByRefObject) clientProxy;
511 static IMessageSink GetClientChannelSinkChain(string url, object channelData, out string objectUri)
513 IMessageSink sink = ChannelServices.CreateClientChannelSinkChain (url, channelData, out objectUri);
518 string msg = String.Format ("Cannot create channel sink to connect to URL {0}. An appropriate channel has probably not been registered.", url);
519 throw new RemotingException (msg);
523 string msg = String.Format ("Cannot create channel sink to connect to the remote object. An appropriate channel has probably not been registered.", url);
524 throw new RemotingException (msg);
530 internal static ClientActivatedIdentity CreateContextBoundObjectIdentity(Type objectType)
532 ClientActivatedIdentity identity = new ClientActivatedIdentity (null, objectType);
533 identity.ChannelSink = ChannelServices.CrossContextChannel;
537 internal static ClientActivatedIdentity CreateClientActivatedServerIdentity(MarshalByRefObject realObject, Type objectType, string objectUri)
539 ClientActivatedIdentity identity = new ClientActivatedIdentity (objectUri, objectType);
540 identity.AttachServerObject (realObject, Context.DefaultContext);
541 RegisterServerIdentity (identity);
542 identity.StartTrackingLifetime ((ILease)realObject.InitializeLifetimeService ());
546 internal static ServerIdentity CreateWellKnownServerIdentity(Type objectType, string objectUri, WellKnownObjectMode mode)
548 ServerIdentity identity;
550 if (mode == WellKnownObjectMode.SingleCall)
551 identity = new SingleCallIdentity(objectUri, Context.DefaultContext, objectType);
553 identity = new SingletonIdentity(objectUri, Context.DefaultContext, objectType);
555 RegisterServerIdentity (identity);
559 private static void RegisterServerIdentity(ServerIdentity identity)
563 if (uri_hash.ContainsKey (identity.ObjectUri))
564 throw new RemotingException ("Uri already in use: " + identity.ObjectUri + ".");
566 uri_hash[identity.ObjectUri] = identity;
570 internal static object GetProxyForRemoteObject (ObjRef objref, Type classToProxy)
572 ClientActivatedIdentity identity = GetIdentityForUri (objref.URI) as ClientActivatedIdentity;
573 if (identity != null) return identity.GetServerObject ();
574 else return GetRemoteObject (objref, classToProxy);
577 internal static object GetRemoteObject(ObjRef objRef, Type proxyType)
580 GetOrCreateClientIdentity (objRef, proxyType, out proxy);
584 internal static object GetDomainProxy(AppDomain domain)
588 Context currentContext = Thread.CurrentContext;
592 data = (byte[])AppDomain.InvokeInDomain (domain, typeof (AppDomain).GetMethod ("GetMarshalledDomainObjRef", BindingFlags.Instance|BindingFlags.NonPublic), domain, null);
596 AppDomain.InternalSetContext (currentContext);
599 MemoryStream stream = new MemoryStream (data);
600 ObjRef appref = (ObjRef) CADSerializer.DeserializeObject (stream);
601 return (AppDomain) RemotingServices.Unmarshal(appref);
604 private static void RegisterInternalChannels()
606 CrossAppDomainChannel.RegisterCrossAppDomainChannel();
609 internal static void DisposeIdentity (Identity ident)
613 if (!ident.Disposed) {
614 ClientIdentity clientId = ident as ClientIdentity;
615 if (clientId != null)
616 uri_hash.Remove (GetNormalizedUri (clientId.TargetUri));
618 uri_hash.Remove (ident.ObjectUri);
620 ident.Disposed = true;
625 internal static Identity GetMessageTargetIdentity (IMessage msg)
627 // Returns the identity where the message is sent
629 if (msg is IInternalMessage)
630 return ((IInternalMessage)msg).TargetIdentity;
634 string uri = GetNormalizedUri (((IMethodMessage)msg).Uri);
635 return uri_hash [uri] as ServerIdentity;
639 internal static void SetMessageTargetIdentity (IMessage msg, Identity ident)
641 if (msg is IInternalMessage)
642 ((IInternalMessage)msg).TargetIdentity = ident;
645 internal static bool UpdateOutArgObject (ParameterInfo pi, object local, object remote)
647 if (local is StringBuilder)
649 StringBuilder sb = local as StringBuilder;
650 sb.Remove (0, sb.Length);
651 sb.Append (remote.ToString());
654 else if (pi.ParameterType.IsArray && ((Array)local).Rank == 1)
656 Array alocal = (Array) local;
657 if (alocal.Rank == 1)
659 Array.Copy ((Array) remote, alocal, alocal.Length);
670 static string GetNormalizedUri (string uri)
672 if (uri.StartsWith ("/")) return uri.Substring (1);