2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Channels / CrossAppDomainChannel.cs
1 //
2 // System.Runtime.Remoting.Channels.CrossAppDomainChannel.cs
3 //
4 // Author: Patrik Torstensson (totte_mono@yahoo.com)
5 //         Lluis Sanchez Gual (lluis@ximian.com)
6 //
7 // 2003 (C) Copyright, Ximian, Inc.
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.Collections;
34 using System.IO;
35 using System.Threading;
36 using System.Runtime.Remoting;
37 using System.Runtime.Remoting.Messaging;   
38 using System.Runtime.Remoting.Channels; 
39 using System.Runtime.Remoting.Contexts; 
40 using System.Runtime.Serialization;
41 using System.Runtime.Serialization.Formatters.Binary;
42 using System.Reflection;
43
44 namespace System.Runtime.Remoting.Channels 
45 {
46
47         // Holds the cross appdomain channel data (used to get/create the correct sink)
48         [Serializable]
49         internal class CrossAppDomainData 
50         {
51                 // TODO: Add context support
52                 // Required for .NET compatibility
53                 private object _ContextID;
54                 private int _DomainID;
55                 private string _processGuid;
56
57                 internal CrossAppDomainData(int domainId) 
58                 {
59                         _ContextID = (int) 0;
60                         _DomainID = domainId;
61                         _processGuid = RemotingConfiguration.ProcessId;
62                 }
63
64                 internal int DomainID 
65                 {  
66                         get { return _DomainID; }
67                 }
68
69                 internal string ProcessID
70                 {
71                         get { return _processGuid; }
72                 }
73         }
74
75         // Responsible for marshalling objects between appdomains
76         [Serializable]
77         internal class CrossAppDomainChannel : IChannel, IChannelSender, IChannelReceiver 
78         {
79                 private const String _strName = "MONOCAD";
80                 
81                 private static Object s_lock = new Object();
82
83                 internal static void RegisterCrossAppDomainChannel() 
84                 {
85                         lock (s_lock) 
86                         {
87                                 // todo: make singleton
88                                 CrossAppDomainChannel monocad = new CrossAppDomainChannel();
89                                 ChannelServices.RegisterChannel ((IChannel) monocad);
90                         }
91                 }               
92
93                 // IChannel implementation
94                 public virtual String ChannelName 
95                 {
96                         get { return _strName; }
97                 }
98     
99                 public virtual int ChannelPriority 
100                 {
101                         get { return 100; }
102                 }
103                 
104                 public String Parse(String url, out String objectURI) 
105                 {
106                         objectURI = url;
107                         return null;
108                 }       
109
110                 // IChannelReceiver
111                 public virtual Object ChannelData 
112                 {
113                         get { return new CrossAppDomainData(Thread.GetDomainID()); }
114                 }       
115                 
116                 public virtual String[] GetUrlsForUri(String objectURI) 
117                 {
118                         throw new NotSupportedException("CrossAppdomain channel dont support UrlsForUri");
119                 }       
120                 
121                 // Dummies
122                 public virtual void StartListening(Object data) {}
123                 public virtual void StopListening(Object data) {}       
124
125                 // IChannelSender
126                 public virtual IMessageSink CreateMessageSink(String url, Object data, out String uri) 
127                 {
128                         uri = null;
129             
130                         if (data != null) 
131                         {
132                                 // Get the data and then get the sink
133                                 CrossAppDomainData cadData = data as CrossAppDomainData;
134                                 if (cadData != null && cadData.ProcessID == RemotingConfiguration.ProcessId)
135                                         // GetSink creates a new sink if we don't have any (use contexts here later)
136                                         return CrossAppDomainSink.GetSink(cadData.DomainID);
137                         } 
138                         if (url != null && url.StartsWith(_strName)) 
139                                 throw new NotSupportedException("Can't create a named channel via crossappdomain");
140
141                         return null;
142                 }
143         }
144         
145         [MonoTODO("Handle domain unloading?")]
146         internal class CrossAppDomainSink : IMessageSink 
147         {
148                 private static Hashtable s_sinks = new Hashtable();
149
150                 private static MethodInfo processMessageMethod =
151                         typeof (CrossAppDomainSink).GetMethod ("ProcessMessageInDomain", BindingFlags.NonPublic|BindingFlags.Static);
152
153
154                 private int _domainID;
155
156                 internal CrossAppDomainSink(int domainID) 
157                 {
158                         _domainID = domainID;
159                 }
160                 
161                 internal static CrossAppDomainSink GetSink(int domainID) 
162                 {
163                         // Check if we have a sink for the current domainID
164                         // note, locking is not to bad here, very few class to GetSink
165                         lock (s_sinks.SyncRoot) 
166                         {
167                                 if (s_sinks.ContainsKey(domainID)) 
168                                         return (CrossAppDomainSink) s_sinks[domainID];
169                                 else 
170                                 {
171                                         CrossAppDomainSink sink = new CrossAppDomainSink(domainID);
172                                         s_sinks[domainID] = sink;
173
174                                         return sink;
175                                 }
176                         }
177                 }
178                 
179                 internal int TargetDomainId {
180                         get { return _domainID; }
181                 }
182
183                 private struct ProcessMessageRes {
184                         public byte[] arrResponse;
185                         public CADMethodReturnMessage cadMrm;
186                 }
187
188 #pragma warning disable 169
189                 private static ProcessMessageRes ProcessMessageInDomain (
190                         byte[] arrRequest,
191                         CADMethodCallMessage cadMsg)
192             {
193                         ProcessMessageRes res = new ProcessMessageRes ();
194
195                         try 
196                         {
197                                 AppDomain.CurrentDomain.ProcessMessageInDomain (arrRequest, cadMsg, out res.arrResponse, out res.cadMrm);
198                         }
199                         catch (Exception e) 
200                         {
201                                 IMessage errorMsg = new MethodResponse (e, new ErrorMessage());
202                                 res.arrResponse = CADSerializer.SerializeMessage (errorMsg).GetBuffer(); 
203                         }
204                         return res;
205                 }
206 #pragma warning restore 169
207
208                 public virtual IMessage SyncProcessMessage(IMessage msgRequest) 
209                 {
210                         IMessage retMessage = null;
211
212                         try 
213                         {
214                                 // Time to transit into the "our" domain
215                                 byte [] arrResponse = null;
216                                 byte [] arrRequest = null; 
217                                 
218                                 CADMethodReturnMessage cadMrm = null;
219                                 CADMethodCallMessage cadMsg;
220                                 
221                                 cadMsg = CADMethodCallMessage.Create (msgRequest);
222                                 if (null == cadMsg) {
223                                         // Serialize the request message
224                                         MemoryStream reqMsgStream = CADSerializer.SerializeMessage(msgRequest);
225                                         arrRequest = reqMsgStream.GetBuffer();
226                                 }
227
228                                 Context currentContext = Thread.CurrentContext;
229
230                                 try {
231                                         // InternalInvoke can't handle out arguments, this is why
232                                         // we return the results in a structure
233                                         ProcessMessageRes res = (ProcessMessageRes)AppDomain.InvokeInDomainByID (_domainID, processMessageMethod, null, new object [] { arrRequest, cadMsg });
234                                         arrResponse = res.arrResponse;
235                                         cadMrm = res.cadMrm;
236                                 } finally {
237                                         AppDomain.InternalSetContext (currentContext);
238                                 }                                       
239
240                                 
241                                 if (null != arrResponse) {
242                                         // Time to deserialize the message
243                                         MemoryStream respMsgStream = new MemoryStream(arrResponse);
244
245                                         // Deserialize the response message
246                                         retMessage = CADSerializer.DeserializeMessage(respMsgStream, msgRequest as IMethodCallMessage);
247                                 } else
248                                         retMessage = new MethodResponse (msgRequest as IMethodCallMessage, cadMrm);
249                         }
250                         catch (Exception e) 
251                         {
252                                 try
253                                 {
254                                         retMessage = new ReturnMessage (e, msgRequest as IMethodCallMessage);
255                                 }
256                                 catch (Exception)
257                                 {
258                                         // this is just to be sure
259                                 }
260                         }
261
262                 return retMessage;
263                 }
264
265                 public virtual IMessageCtrl AsyncProcessMessage (IMessage reqMsg, IMessageSink replySink) 
266                 {
267                         AsyncRequest req = new AsyncRequest (reqMsg, replySink);
268                         ThreadPool.QueueUserWorkItem (new WaitCallback (SendAsyncMessage), req);
269                         return null;
270                 }
271                 
272                 public void SendAsyncMessage (object data)
273                 {
274                         AsyncRequest req = (AsyncRequest)data;
275                         IMessage response = SyncProcessMessage (req.MsgRequest);
276                         req.ReplySink.SyncProcessMessage (response);
277                 }
278                 
279                 public IMessageSink NextSink { get { return null; } }
280         }
281
282         internal class CADSerializer 
283         {
284                 internal static IMessage DeserializeMessage(MemoryStream mem, IMethodCallMessage msg)
285                 {
286                         BinaryFormatter serializer = new BinaryFormatter();                
287
288                         serializer.SurrogateSelector = null;
289                         mem.Position = 0;
290
291                         if (msg == null)
292                                 return (IMessage) serializer.Deserialize(mem, null);
293                         else
294                                 return (IMessage) serializer.DeserializeMethodResponse(mem, null, msg);
295                 }
296                 
297                 internal static MemoryStream SerializeMessage(IMessage msg)
298                 {
299                         MemoryStream mem = new MemoryStream ();
300                         BinaryFormatter serializer = new BinaryFormatter ();                
301
302                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();
303                         serializer.Serialize (mem, msg);
304
305                         mem.Position = 0;
306
307                         return mem;
308                 }
309
310                 internal static MemoryStream SerializeObject(object obj)
311                 {
312                         MemoryStream mem = new MemoryStream ();
313                         BinaryFormatter serializer = new BinaryFormatter ();                
314
315                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();
316                         serializer.Serialize (mem, obj);
317
318                         mem.Position = 0;
319
320                         return mem;
321                 }
322
323                 internal static object DeserializeObject(MemoryStream mem)
324                 {
325                         BinaryFormatter serializer = new BinaryFormatter();                
326
327                         serializer.SurrogateSelector = null;
328                         mem.Position = 0;
329
330                         return serializer.Deserialize (mem);
331                 }
332         }
333         
334         internal class AsyncRequest
335         {
336                 internal IMessageSink ReplySink;
337                 internal IMessage MsgRequest;
338                 
339                 public AsyncRequest (IMessage msgRequest, IMessageSink replySink)
340                 {
341                         ReplySink = replySink;
342                         MsgRequest = msgRequest;
343                 }
344         }
345
346 }