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 return ident is ClientIdentity;
368 public static bool IsObjectOutOfContext(object tp)
370 // TODO: use internal call for better performance
371 Identity ident = GetObjectIdentity((MarshalByRefObject)tp);
372 if (ident == null) return false;
374 ServerIdentity sident = ident as ServerIdentity;
375 if (sident != null) return sident.Context != System.Threading.Thread.CurrentContext;
379 public static bool IsOneWay(MethodBase method)
381 // TODO: use internal call for better performance
382 object[] atts = method.GetCustomAttributes (typeof (OneWayAttribute), false);
383 return atts.Length > 0;
386 internal static bool IsAsyncMessage(IMessage msg)
388 if (! (msg is MonoMethodMessage)) return false;
389 else if (((MonoMethodMessage)msg).IsAsync) return true;
390 else if (IsOneWay (((MonoMethodMessage)msg).MethodBase)) return true;
394 public static void SetObjectUriForMarshal(MarshalByRefObject obj, string uri)
396 if (IsTransparentProxy (obj)) {
397 RealProxy proxy = RemotingServices.GetRealProxy(obj);
398 Identity identity = proxy.ObjectIdentity;
400 if (identity != null && !(identity is ServerIdentity) && !proxy.GetProxiedType().IsContextful)
401 throw new RemotingException ("SetObjectUriForMarshal method should only be called for MarshalByRefObjects that exist in the current AppDomain.");
407 #region Internal Methods
409 internal static object CreateClientProxy (ActivatedClientTypeEntry entry, object[] activationAttributes)
411 if (entry.ContextAttributes != null || activationAttributes != null)
413 ArrayList props = new ArrayList ();
414 if (entry.ContextAttributes != null) props.AddRange (entry.ContextAttributes);
415 if (activationAttributes != null) props.AddRange (activationAttributes);
416 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, props.ToArray ());
419 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, null);
422 internal static object CreateClientProxy (Type objectType, string url, object[] activationAttributes)
424 string activationUrl = url + "/RemoteActivationService.rem";
427 IMessageSink sink = GetClientChannelSinkChain (activationUrl, null, out objectUri);
429 RemotingProxy proxy = new RemotingProxy (objectType, activationUrl, activationAttributes);
430 return proxy.GetTransparentProxy();
433 internal static object CreateClientProxy (WellKnownClientTypeEntry entry)
435 return Connect (entry.ObjectType, entry.ObjectUrl, null);
438 internal static object CreateClientProxyForContextBound (Type type, object[] activationAttributes)
440 if (type.IsContextful)
442 // Look for a ProxyAttribute
443 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (type, typeof(ProxyAttribute), true);
445 return att.CreateInstance (type);
447 RemotingProxy proxy = new RemotingProxy (type, ChannelServices.CrossContextUrl, activationAttributes);
448 return proxy.GetTransparentProxy();
451 internal static Identity GetIdentityForUri (string uri)
455 return (Identity)uri_hash [GetNormalizedUri(uri)];
459 internal static Identity GetObjectIdentity (MarshalByRefObject obj)
461 if (IsTransparentProxy(obj))
462 return GetRealProxy (obj).ObjectIdentity;
464 return obj.ObjectIdentity;
467 internal static ClientIdentity GetOrCreateClientIdentity(ObjRef objRef, Type proxyType, out object clientProxy)
469 // This method looks for an identity for the given url.
470 // If an identity is not found, it creates the identity and
471 // assigns it a proxy to the remote object.
473 // Creates the client sink chain for the given url or channelData.
474 // It will also get the object uri from the url.
476 object channelData = objRef.ChannelInfo != null ? objRef.ChannelInfo.ChannelData : null;
477 string url = (channelData == null) ? objRef.URI : null;
480 IMessageSink sink = GetClientChannelSinkChain (url, channelData, out objectUri);
482 if (objectUri == null) objectUri = objRef.URI;
487 string uri = GetNormalizedUri (objRef.URI);
489 ClientIdentity identity = uri_hash [uri] as ClientIdentity;
490 if (identity != null)
492 // Object already registered
493 clientProxy = identity.ClientProxy;
494 if (clientProxy != null) return identity;
496 // The proxy has just been GCed, so its identity cannot
497 // be reused. Just dispose it.
498 DisposeIdentity (identity);
501 // Creates an identity and a proxy for the remote object
503 identity = new ClientIdentity (objectUri, objRef);
504 identity.ChannelSink = sink;
506 // Registers the identity
507 uri_hash [uri] = identity;
509 if (proxyType != null)
511 RemotingProxy proxy = new RemotingProxy (proxyType, identity);
512 clientProxy = proxy.GetTransparentProxy();
513 identity.ClientProxy = (MarshalByRefObject) clientProxy;
520 static IMessageSink GetClientChannelSinkChain(string url, object channelData, out string objectUri)
522 IMessageSink sink = ChannelServices.CreateClientChannelSinkChain (url, channelData, out objectUri);
527 string msg = String.Format ("Cannot create channel sink to connect to URL {0}. An appropriate channel has probably not been registered.", url);
528 throw new RemotingException (msg);
532 string msg = String.Format ("Cannot create channel sink to connect to the remote object. An appropriate channel has probably not been registered.", url);
533 throw new RemotingException (msg);
539 internal static ClientActivatedIdentity CreateContextBoundObjectIdentity(Type objectType)
541 ClientActivatedIdentity identity = new ClientActivatedIdentity (null, objectType);
542 identity.ChannelSink = ChannelServices.CrossContextChannel;
546 internal static ClientActivatedIdentity CreateClientActivatedServerIdentity(MarshalByRefObject realObject, Type objectType, string objectUri)
548 ClientActivatedIdentity identity = new ClientActivatedIdentity (objectUri, objectType);
549 identity.AttachServerObject (realObject, Context.DefaultContext);
550 RegisterServerIdentity (identity);
551 identity.StartTrackingLifetime ((ILease)realObject.InitializeLifetimeService ());
555 internal static ServerIdentity CreateWellKnownServerIdentity(Type objectType, string objectUri, WellKnownObjectMode mode)
557 ServerIdentity identity;
559 if (mode == WellKnownObjectMode.SingleCall)
560 identity = new SingleCallIdentity(objectUri, Context.DefaultContext, objectType);
562 identity = new SingletonIdentity(objectUri, Context.DefaultContext, objectType);
564 RegisterServerIdentity (identity);
568 private static void RegisterServerIdentity(ServerIdentity identity)
572 if (uri_hash.ContainsKey (identity.ObjectUri))
573 throw new RemotingException ("Uri already in use: " + identity.ObjectUri + ".");
575 uri_hash[identity.ObjectUri] = identity;
579 internal static object GetProxyForRemoteObject (ObjRef objref, Type classToProxy)
581 ClientActivatedIdentity identity = GetIdentityForUri (objref.URI) as ClientActivatedIdentity;
582 if (identity != null) return identity.GetServerObject ();
583 else return GetRemoteObject (objref, classToProxy);
586 internal static object GetRemoteObject(ObjRef objRef, Type proxyType)
589 GetOrCreateClientIdentity (objRef, proxyType, out proxy);
593 internal static object GetDomainProxy(AppDomain domain)
597 Context currentContext = Thread.CurrentContext;
601 data = (byte[])AppDomain.InvokeInDomain (domain, typeof (AppDomain).GetMethod ("GetMarshalledDomainObjRef", BindingFlags.Instance|BindingFlags.NonPublic), domain, null);
605 AppDomain.InternalSetContext (currentContext);
608 MemoryStream stream = new MemoryStream (data);
609 ObjRef appref = (ObjRef) CADSerializer.DeserializeObject (stream);
610 return (AppDomain) RemotingServices.Unmarshal(appref);
613 private static void RegisterInternalChannels()
615 CrossAppDomainChannel.RegisterCrossAppDomainChannel();
618 internal static void DisposeIdentity (Identity ident)
622 if (!ident.Disposed) {
623 ClientIdentity clientId = ident as ClientIdentity;
624 if (clientId != null)
625 uri_hash.Remove (GetNormalizedUri (clientId.TargetUri));
627 uri_hash.Remove (ident.ObjectUri);
629 ident.Disposed = true;
634 internal static Identity GetMessageTargetIdentity (IMessage msg)
636 // Returns the identity where the message is sent
638 if (msg is IInternalMessage)
639 return ((IInternalMessage)msg).TargetIdentity;
643 string uri = GetNormalizedUri (((IMethodMessage)msg).Uri);
644 return uri_hash [uri] as ServerIdentity;
648 internal static void SetMessageTargetIdentity (IMessage msg, Identity ident)
650 if (msg is IInternalMessage)
651 ((IInternalMessage)msg).TargetIdentity = ident;
654 internal static bool UpdateOutArgObject (ParameterInfo pi, object local, object remote)
656 if (local is StringBuilder)
658 StringBuilder sb = local as StringBuilder;
659 sb.Remove (0, sb.Length);
660 sb.Append (remote.ToString());
663 else if (pi.ParameterType.IsArray && ((Array)local).Rank == 1)
665 Array alocal = (Array) local;
666 if (alocal.Rank == 1)
668 Array.Copy ((Array) remote, alocal, alocal.Length);
679 static string GetNormalizedUri (string uri)
681 if (uri.StartsWith ("/")) return uri.Substring (1);