2004-11-04 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Channels / ChannelServices.cs
index ce36fbf5d658e769e0f3c4cbd55fd4dee0723f6b..124d01a44294f656c7278191d65f4bf86d5acec5 100644 (file)
 //
 // Author: Rodrigo Moya (rodrigo@ximian.com)
 //         Dietmar Maurer (dietmar@ximian.com)
+//         Lluis Sanchez Gual (lluis@ideary.com)
 //
 // 2002 (C) Copyright, Ximian, Inc.
 //
 
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System.Collections;
+using System.Reflection;
+using System.Runtime.Remoting;
+using System.Runtime.Remoting.Channels;
 using System.Runtime.Remoting.Messaging;
+using System.Runtime.Remoting.Contexts;
 
-namespace System.Runtime.Remoting.Channels
+namespace System.Runtime.Remoting
 {
-       internal class ChannelInfoStore : IChannelInfo
+       [Serializable]
+       internal class ChannelInfo : IChannelInfo
        {
-               object [] data = null;
+               object [] channelData = null;
 
-               public ChannelInfoStore ()
+               public ChannelInfo ()
                {
-                       this.data = ChannelServices.GetCurrentChannelInfo ();
+                       channelData = ChannelServices.GetCurrentChannelInfo ();
                }
-               
-               public object[] ChannelData {
 
+               public ChannelInfo (object remoteChannelData)
+               {
+                       channelData = new object[] { remoteChannelData };
+               }
+               
+               public object[] ChannelData 
+               {
                        get {
-                               return data;
+                               return channelData;
                        }
                        
                        set {
-                               data = value;
+                               channelData = value;
                        }
                }
        }
-       
+}
+
+namespace System.Runtime.Remoting.Channels
+{
        public sealed class ChannelServices
        {
                private static ArrayList registeredChannels = new ArrayList ();
+               private static ArrayList delayedClientChannels = new ArrayList ();
                
+               private static CrossContextChannel _crossContextSink = new CrossContextChannel();
+               
+               internal static string CrossContextUrl = "__CrossContext";
+
                private ChannelServices ()
                {
                }
-               
-               public static IChannel[] RegisteredChannels
+
+               internal static CrossContextChannel CrossContextChannel
                {
-                       get {
-                               IChannel[] channels = new IChannel[registeredChannels.Count];
+                       get { return _crossContextSink; }
+               }
+
+               internal static IMessageSink CreateClientChannelSinkChain(string url, object remoteChannelData, out string objectUri)
+               {
+                       // Locate a channel that can parse the url. This channel will be used to
+                       // create the sink chain.
 
-                               for (int i = 0; i < registeredChannels.Count; i++)
-                                       channels[i] = (IChannel) registeredChannels[i];
+                       object[] channelDataArray = (object[])remoteChannelData;
 
-                               return channels;
+                       lock (registeredChannels.SyncRoot)
+                       {
+                               // First of all, try registered channels
+                               foreach (IChannel c in registeredChannels) 
+                               {
+                                       IChannelSender sender = c as IChannelSender;
+                                       if (sender == null) continue;
+       
+                                       IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
+                                       if (sink != null) return sink;
+                               }
+                               
+                               // Not found. Try now creation delayed channels
+                               RemotingConfiguration.LoadDefaultDelayedChannels ();
+                               foreach (IChannelSender sender in delayedClientChannels) 
+                               {
+                                       IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
+                                       if (sink != null) {
+                                               delayedClientChannels.Remove (sender);
+                                               RegisterChannel (sender);
+                                               return sink;
+                                       }
+                               }
                        }
+                       
+                       objectUri = null;
+                       return null;
                }
-
-               [MonoTODO]
-               public static IMessageCtrl AsyncDispatchMessage (IMessage msg,
-                                                                IMessageSink replySink)
+               
+               internal static IMessageSink CreateClientChannelSinkChain (IChannelSender sender, string url, object[] channelDataArray, out string objectUri)
+               {
+                       objectUri = null;
+                       if (channelDataArray == null) {
+                               return sender.CreateMessageSink (url, null, out objectUri);
+                       }
+                       else {
+                               foreach (object data in channelDataArray) {
+                                       IMessageSink sink = sender.CreateMessageSink (url, data, out objectUri);
+                                       if (sink != null) return sink;          
+                               }
+                       }
+                       return null;
+               }
+               
+               public static IChannel[] RegisteredChannels
                {
-                       throw new NotImplementedException ();
+                       get {
+                               lock (registeredChannels.SyncRoot)
+                               {
+                                       IChannel[] channels = new IChannel[registeredChannels.Count];
+       
+                                       for (int i = 0; i < registeredChannels.Count; i++)
+                                               channels[i] = (IChannel) registeredChannels[i];
+       
+                                       return channels;
+                               }
+                       }
                }
 
                public static IServerChannelSink CreateServerChannelSinkChain (
-                       IServerChannelSinkProvider provider,
-                       IChannelReceiver channel)
-               {
+                       IServerChannelSinkProvider provider, IChannelReceiver channel)
+           {
                        IServerChannelSinkProvider tmp = provider;
                        while (tmp.Next != null) tmp = tmp.Next;
                        tmp.Next = new ServerDispatchSinkProvider ();
 
+                       // Every provider has to call CreateSink() of its next provider
                        return  provider.CreateSink (channel);
                }
 
-               [MonoTODO]
                public static ServerProcessing DispatchMessage (
                        IServerChannelSinkStack sinkStack,
                        IMessage msg,
                        out IMessage replyMsg)
                {
-                       throw new NotImplementedException ();
+                       if (msg == null) throw new ArgumentNullException ("msg");
+                       
+                       // Async processing is not done here because there isn't any way
+                       // to know if a message is to be dispatched sync or asynchronously.
+
+                       replyMsg = SyncDispatchMessage (msg);
+
+                       if (RemotingServices.IsOneWay (((IMethodMessage) msg).MethodBase))
+                               return ServerProcessing.OneWay;
+                       else
+                               return ServerProcessing.Complete;
                }
 
-               [MonoTODO]
                public static IChannel GetChannel (string name)
-               {
-                       throw new NotImplementedException ();
+               {
+                       lock (registeredChannels.SyncRoot)
+                       {
+                               foreach (IChannel chnl in registeredChannels) {
+                                       if (chnl.ChannelName == name && !(chnl is CrossAppDomainChannel)) return chnl;
+                               }
+                               return null;
+                       }
                }
 
-               [MonoTODO]
                public static IDictionary GetChannelSinkProperties (object obj)
                {
-                       throw new NotImplementedException ();
+                       if (!RemotingServices.IsTransparentProxy (obj))
+                               throw new ArgumentException ("obj must be a proxy","obj");
+                               
+                       ClientIdentity ident = (ClientIdentity) RemotingServices.GetRealProxy (obj).ObjectIdentity;
+                       IMessageSink sink = ident.ChannelSink;
+                       ArrayList dics = new ArrayList ();
+                       
+                       while (sink != null && !(sink is IClientChannelSink))
+                               sink = sink.NextSink;
+
+                       if (sink == null)
+                               return new Hashtable ();
+
+                       IClientChannelSink csink = sink as IClientChannelSink;
+                       while (csink != null)
+                       {
+                               dics.Add (csink.Properties);
+                               csink = csink.NextChannelSink;
+                       }
+
+                       IDictionary[] adics = (IDictionary[]) dics.ToArray (typeof(IDictionary[]));
+                       return new AggregateDictionary (adics);
                }
 
-               [MonoTODO]
                public static string[] GetUrlsForObject (MarshalByRefObject obj)
                {
-                       throw new NotImplementedException ();
+                       string uri = RemotingServices.GetObjectUri (obj);
+                       if (uri == null) return new string [0];
+
+                       ArrayList list = new ArrayList ();
+
+                       lock (registeredChannels.SyncRoot)
+                       {
+                               foreach (object chnl_obj in registeredChannels) {
+                                       if (chnl_obj is CrossAppDomainChannel) continue;
+                                       
+                                       IChannelReceiver chnl = chnl_obj as IChannelReceiver;
+       
+                                       if (chnl != null)
+                                               list.AddRange (chnl.GetUrlsForUri (uri));
+                               }
+                       }
+                       
+                       return  (string[]) list.ToArray (typeof(string));
                }
 
                public static void RegisterChannel (IChannel chnl)
-               {
-                       // fixme: sort it by priority
-                       registeredChannels.Add ((object) chnl);
+               {
+                       // Put the channel in the correct place according to its priority.
+                       // Since there are not many channels, a linear search is ok.
+
+                       lock (registeredChannels.SyncRoot)
+                       {
+                               int pos = -1;
+                               for (int n = 0; n < registeredChannels.Count; n++) 
+                               {
+                                       IChannel regc = (IChannel) registeredChannels[n];
+                                       
+                                       if (regc.ChannelName == chnl.ChannelName && chnl.ChannelName != "")
+                                               throw new RemotingException ("Channel " + regc.ChannelName + " already registered");
+                                               
+                                       if (regc.ChannelPriority < chnl.ChannelPriority && pos==-1)
+                                               pos = n;
+                               }
+                               
+                               if (pos != -1) registeredChannels.Insert (pos, chnl);
+                               else registeredChannels.Add (chnl);
+                               
+                               IChannelReceiver receiver = chnl as IChannelReceiver;
+                               if (receiver != null) receiver.StartListening (null);
+                       }
+               }
+
+               internal static void RegisterChannelConfig (ChannelData channel)
+               {
+                       IServerChannelSinkProvider serverSinks = null;
+                       IClientChannelSinkProvider clientSinks = null;
+                       
+                       // Create server providers
+                       for (int n=channel.ServerProviders.Count-1; n>=0; n--)
+                       {
+                               ProviderData prov = channel.ServerProviders[n] as ProviderData;
+                               IServerChannelSinkProvider sinkp = (IServerChannelSinkProvider) CreateProvider (prov);
+                               sinkp.Next = serverSinks;
+                               serverSinks = sinkp;
+                       }
+                       
+                       // Create client providers
+                       for (int n=channel.ClientProviders.Count-1; n>=0; n--)
+                       {
+                               ProviderData prov = channel.ClientProviders[n] as ProviderData;
+                               IClientChannelSinkProvider sinkp = (IClientChannelSinkProvider) CreateProvider (prov);
+                               sinkp.Next = clientSinks;
+                               clientSinks = sinkp;
+                       }
+
+                       // Create the channel
+                       
+                       Type type = Type.GetType (channel.Type);
+                       if (type == null) throw new RemotingException ("Type '" + channel.Type + "' not found");
+                       
+                       Object[] parms;                 
+                       Type[] signature;                       
+                       bool clienc = typeof (IChannelSender).IsAssignableFrom (type);
+                       bool serverc = typeof (IChannelReceiver).IsAssignableFrom (type);
+                       
+                       if (clienc && serverc) {
+                               signature = new Type [] {typeof(IDictionary), typeof(IClientChannelSinkProvider), typeof(IServerChannelSinkProvider)};
+                               parms = new Object[] {channel.CustomProperties, clientSinks, serverSinks};
+                       }
+                       else if (clienc) {
+                               signature = new Type [] {typeof(IDictionary), typeof(IClientChannelSinkProvider)};
+                               parms = new Object[] {channel.CustomProperties, clientSinks};
+                       }
+                       else if (serverc) {
+                               signature = new Type [] {typeof(IDictionary), typeof(IServerChannelSinkProvider)};
+                               parms = new Object[] {channel.CustomProperties, serverSinks};
+                       }
+                       else
+                               throw new RemotingException (type + " is not a valid channel type");
+                               
+                       ConstructorInfo ctor = type.GetConstructor (signature);
+                       if (ctor == null)
+                               throw new RemotingException (type + " does not have a valid constructor");
+
+                       IChannel ch;
+                       try
+                       {
+                               ch = (IChannel) ctor.Invoke (parms);
+                       }
+                       catch (TargetInvocationException ex)
+                       {
+                               throw ex.InnerException;
+                       }
+                       
+                       lock (registeredChannels.SyncRoot)
+                       {
+                               if (channel.DelayLoadAsClientChannel == "true" && !(ch is IChannelReceiver))
+                                       delayedClientChannels.Add (ch);
+                               else
+                                       RegisterChannel (ch);
+                       }
+               }
+               
+               static object CreateProvider (ProviderData prov)
+               {
+                       Type pvtype = Type.GetType (prov.Type);
+                       if (pvtype == null) throw new RemotingException ("Type '" + prov.Type + "' not found");
+                       Object[] pvparms = new Object[] {prov.CustomProperties, prov.CustomData};
+                       
+                       try
+                       {
+                               return Activator.CreateInstance (pvtype, pvparms);
+                       }
+                       catch (Exception ex)
+                       {
+                               if (ex is TargetInvocationException) ex = ((TargetInvocationException)ex).InnerException;
+                               throw new RemotingException ("An instance of provider '" + pvtype + "' could not be created: " + ex.Message);
+                       }
                }
 
-               [MonoTODO]
                public static IMessage SyncDispatchMessage (IMessage msg)
                {
-                       throw new NotImplementedException ();
+                       IMessage ret = CheckIncomingMessage (msg);
+                       if (ret != null) return CheckReturnMessage (msg, ret);
+                       ret = _crossContextSink.SyncProcessMessage (msg);
+                       return CheckReturnMessage (msg, ret);
+               }
+
+               public static IMessageCtrl AsyncDispatchMessage (IMessage msg, IMessageSink replySink)
+               {
+                       IMessage ret = CheckIncomingMessage (msg);
+                       if (ret != null) {
+                               replySink.SyncProcessMessage (CheckReturnMessage (msg, ret));
+                               return null;
+                       }
+                       
+#if NET_1_1
+                       if (RemotingConfiguration.CustomErrorsEnabled (IsLocalCall (msg)))
+                               replySink = new ExceptionFilterSink (msg, replySink);
+#endif
+                       
+                       return _crossContextSink.AsyncProcessMessage (msg, replySink);          
+               }
+               
+               static ReturnMessage CheckIncomingMessage (IMessage msg)
+               {
+                       IMethodMessage call = (IMethodMessage)msg;
+                       ServerIdentity identity = RemotingServices.GetIdentityForUri (call.Uri) as ServerIdentity;
+
+                       if (identity == null) 
+                               return new ReturnMessage (new RemotingException ("No receiver for uri " + call.Uri), (IMethodCallMessage) msg);
+
+                       RemotingServices.SetMessageTargetIdentity (msg, identity);
+                       return null;
+               }
+
+               internal static IMessage CheckReturnMessage (IMessage callMsg, IMessage retMsg)
+               {
+#if NET_1_1
+                       IMethodReturnMessage ret = retMsg as IMethodReturnMessage;
+                       if (ret != null && ret.Exception != null)
+                       {
+                               if (RemotingConfiguration.CustomErrorsEnabled (IsLocalCall (callMsg)))
+                               {
+                                       Exception ex = new Exception ("Server encountered an internal error. For more information, turn off customErrors in the server's .config file.");
+                                       retMsg = new MethodResponse (ex, (IMethodCallMessage)callMsg);
+                               }
+                       }
+#endif
+                       return retMsg;
+               }
+               
+               static bool IsLocalCall (IMessage callMsg)
+               {
+                       return true;
+                       
+/*                     How can I know if a call is local?!?
+                       
+                       object isLocal = callMsg.Properties ["__isLocalCall"];
+                       if (isLocal == null) return false;
+                       return (bool)isLocal;
+*/
                }
 
                public static void UnregisterChannel (IChannel chnl)
                {
                        if (chnl == null)
                                throw new ArgumentNullException ();
-                       if (!registeredChannels.Contains ((object) chnl))
-                               throw new RemotingException ();
-
-                       registeredChannels.Remove ((object) chnl);
+                               
+                       lock (registeredChannels.SyncRoot)
+                       {
+                               for (int n=0; n<registeredChannels.Count; n++) 
+                               {
+                                       if (registeredChannels [n] == (object)chnl) {
+                                               registeredChannels.RemoveAt (n);
+                                               IChannelReceiver chnlReceiver = chnl as IChannelReceiver;
+                                               if(chnlReceiver != null)
+                                                       chnlReceiver.StopListening(null);
+                                               return;
+                                       }
+                               }
+                               
+                               throw new RemotingException ("Channel not registered");
+       
+                       }
                }
 
                internal static object [] GetCurrentChannelInfo ()
                {
                        ArrayList list = new ArrayList ();
                        
-                       foreach (object chnl_obj in registeredChannels) {
-                               IChannelReceiver chnl = chnl_obj as IChannelReceiver;
-                       
-                               if (chnl != null) {
-                                       object chnl_data = chnl.ChannelData;
-                                       if (chnl_data != null)
-                                               list.Add (chnl_data);
+                       lock (registeredChannels.SyncRoot)
+                       {
+                               foreach (object chnl_obj in registeredChannels) {
+                                       IChannelReceiver chnl = chnl_obj as IChannelReceiver;
+                               
+                                       if (chnl != null) {
+                                               object chnl_data = chnl.ChannelData;
+                                               if (chnl_data != null)
+                                                       list.Add (chnl_data);
+                                       }
                                }
                        }
-
+                       
                        return  list.ToArray ();
                }
        }
+       
+       internal class ExceptionFilterSink: IMessageSink
+       {
+               IMessageSink _next;
+               IMessage _call;
+               
+               public ExceptionFilterSink (IMessage call, IMessageSink next)
+               {
+                       _call = call;
+                       _next = next;
+               }
+               
+               public IMessage SyncProcessMessage (IMessage msg)
+               {
+                       return _next.SyncProcessMessage (ChannelServices.CheckReturnMessage (_call, msg));
+               }
+
+               public IMessageCtrl AsyncProcessMessage (IMessage msg, IMessageSink replySink)
+               {
+                       throw new InvalidOperationException();
+               }
+
+               public IMessageSink NextSink 
+               { 
+                       get { return _next; }
+               }
+       }
 }