importing messaging-2008 branch to trunk [continued]
[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                 private const String _strBaseURI = "MONOCADURI";
81                 
82                 private static Object s_lock = new Object();
83
84                 internal static void RegisterCrossAppDomainChannel() 
85                 {
86                         lock (s_lock) 
87                         {
88                                 // todo: make singleton
89                                 CrossAppDomainChannel monocad = new CrossAppDomainChannel();
90                                 ChannelServices.RegisterChannel ((IChannel) monocad);
91                         }
92                 }               
93
94                 // IChannel implementation
95                 public virtual String ChannelName 
96                 {
97                         get { return _strName; }
98                 }
99     
100                 public virtual int ChannelPriority 
101                 {
102                         get { return 100; }
103                 }
104                 
105                 public String Parse(String url, out String objectURI) 
106                 {
107                         objectURI = url;
108                         return null;
109                 }       
110
111                 // IChannelReceiver
112                 public virtual Object ChannelData 
113                 {
114                         get { return new CrossAppDomainData(Thread.GetDomainID()); }
115                 }       
116                 
117                 public virtual String[] GetUrlsForUri(String objectURI) 
118                 {
119                         throw new NotSupportedException("CrossAppdomain channel dont support UrlsForUri");
120                 }       
121                 
122                 // Dummies
123                 public virtual void StartListening(Object data) {}
124                 public virtual void StopListening(Object data) {}       
125
126                 // IChannelSender
127                 public virtual IMessageSink CreateMessageSink(String url, Object data, out String uri) 
128                 {
129                         uri = null;
130             
131                         if (data != null) 
132                         {
133                                 // Get the data and then get the sink
134                                 CrossAppDomainData cadData = data as CrossAppDomainData;
135                                 if (cadData != null && cadData.ProcessID == RemotingConfiguration.ProcessId)
136                                         // GetSink creates a new sink if we don't have any (use contexts here later)
137                                         return CrossAppDomainSink.GetSink(cadData.DomainID);
138                         } 
139                         if (url != null && url.StartsWith(_strName)) 
140                                 throw new NotSupportedException("Can't create a named channel via crossappdomain");
141
142                         return null;
143                 }
144         }
145         
146         [MonoTODO("Handle domain unloading?")]
147         internal class CrossAppDomainSink : IMessageSink 
148         {
149                 private static Hashtable s_sinks = new Hashtable();
150
151                 private static MethodInfo processMessageMethod =
152                         typeof (CrossAppDomainSink).GetMethod ("ProcessMessageInDomain", BindingFlags.NonPublic|BindingFlags.Static);
153
154
155                 private int _domainID;
156
157                 internal CrossAppDomainSink(int domainID) 
158                 {
159                         _domainID = domainID;
160                 }
161                 
162                 internal static CrossAppDomainSink GetSink(int domainID) 
163                 {
164                         // Check if we have a sink for the current domainID
165                         // note, locking is not to bad here, very few class to GetSink
166                         lock (s_sinks.SyncRoot) 
167                         {
168                                 if (s_sinks.ContainsKey(domainID)) 
169                                         return (CrossAppDomainSink) s_sinks[domainID];
170                                 else 
171                                 {
172                                         CrossAppDomainSink sink = new CrossAppDomainSink(domainID);
173                                         s_sinks[domainID] = sink;
174
175                                         return sink;
176                                 }
177                         }
178                 }
179                 
180                 internal int TargetDomainId {
181                         get { return _domainID; }
182                 }
183
184                 private struct ProcessMessageRes {
185                         public byte[] arrResponse;
186                         public CADMethodReturnMessage cadMrm;
187                 }
188
189 #pragma warning disable 169
190                 private static ProcessMessageRes ProcessMessageInDomain (
191                         byte[] arrRequest,
192                         CADMethodCallMessage cadMsg)
193             {
194                         ProcessMessageRes res = new ProcessMessageRes ();
195
196                         try 
197                         {
198                                 AppDomain.CurrentDomain.ProcessMessageInDomain (arrRequest, cadMsg, out res.arrResponse, out res.cadMrm);
199                         }
200                         catch (Exception e) 
201                         {
202                                 IMessage errorMsg = new MethodResponse (e, new ErrorMessage());
203                                 res.arrResponse = CADSerializer.SerializeMessage (errorMsg).GetBuffer(); 
204                         }
205                         return res;
206                 }
207 #pragma warning restore 169
208
209                 public virtual IMessage SyncProcessMessage(IMessage msgRequest) 
210                 {
211                         IMessage retMessage = null;
212
213                         try 
214                         {
215                                 // Time to transit into the "our" domain
216                                 byte [] arrResponse = null;
217                                 byte [] arrRequest = null; 
218                                 
219                                 CADMethodReturnMessage cadMrm = null;
220                                 CADMethodCallMessage cadMsg;
221                                 
222                                 cadMsg = CADMethodCallMessage.Create (msgRequest);
223                                 if (null == cadMsg) {
224                                         // Serialize the request message
225                                         MemoryStream reqMsgStream = CADSerializer.SerializeMessage(msgRequest);
226                                         arrRequest = reqMsgStream.GetBuffer();
227                                 }
228
229                                 Context currentContext = Thread.CurrentContext;
230
231                                 try {
232                                         // InternalInvoke can't handle out arguments, this is why
233                                         // we return the results in a structure
234                                         ProcessMessageRes res = (ProcessMessageRes)AppDomain.InvokeInDomainByID (_domainID, processMessageMethod, null, new object [] { arrRequest, cadMsg });
235                                         arrResponse = res.arrResponse;
236                                         cadMrm = res.cadMrm;
237                                 } finally {
238                                         AppDomain.InternalSetContext (currentContext);
239                                 }                                       
240
241                                 
242                                 if (null != arrResponse) {
243                                         // Time to deserialize the message
244                                         MemoryStream respMsgStream = new MemoryStream(arrResponse);
245
246                                         // Deserialize the response message
247                                         retMessage = CADSerializer.DeserializeMessage(respMsgStream, msgRequest as IMethodCallMessage);
248                                 } else
249                                         retMessage = new MethodResponse (msgRequest as IMethodCallMessage, cadMrm);
250                         }
251                         catch (Exception e) 
252                         {
253                                 try
254                                 {
255                                         retMessage = new ReturnMessage (e, msgRequest as IMethodCallMessage);
256                                 }
257                                 catch (Exception)
258                                 {
259                                         // this is just to be sure
260                                 }
261                         }
262
263                 return retMessage;
264                 }
265
266                 public virtual IMessageCtrl AsyncProcessMessage (IMessage reqMsg, IMessageSink replySink) 
267                 {
268                         AsyncRequest req = new AsyncRequest (reqMsg, replySink);
269                         ThreadPool.QueueUserWorkItem (new WaitCallback (SendAsyncMessage), req);
270                         return null;
271                 }
272                 
273                 public void SendAsyncMessage (object data)
274                 {
275                         AsyncRequest req = (AsyncRequest)data;
276                         IMessage response = SyncProcessMessage (req.MsgRequest);
277                         req.ReplySink.SyncProcessMessage (response);
278                 }
279                 
280                 public IMessageSink NextSink { get { return null; } }
281         }
282
283         internal class CADSerializer 
284         {
285                 internal static IMessage DeserializeMessage(MemoryStream mem, IMethodCallMessage msg)
286                 {
287                         BinaryFormatter serializer = new BinaryFormatter();                
288
289                         serializer.SurrogateSelector = null;
290                         mem.Position = 0;
291
292                         if (msg == null)
293                                 return (IMessage) serializer.Deserialize(mem, null);
294                         else
295                                 return (IMessage) serializer.DeserializeMethodResponse(mem, null, msg);
296                 }
297                 
298                 internal static MemoryStream SerializeMessage(IMessage msg)
299                 {
300                         MemoryStream mem = new MemoryStream ();
301                         BinaryFormatter serializer = new BinaryFormatter ();                
302
303                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();
304                         serializer.Serialize (mem, msg);
305
306                         mem.Position = 0;
307
308                         return mem;
309                 }
310
311                 internal static MemoryStream SerializeObject(object obj)
312                 {
313                         MemoryStream mem = new MemoryStream ();
314                         BinaryFormatter serializer = new BinaryFormatter ();                
315
316                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();
317                         serializer.Serialize (mem, obj);
318
319                         mem.Position = 0;
320
321                         return mem;
322                 }
323
324                 internal static object DeserializeObject(MemoryStream mem)
325                 {
326                         BinaryFormatter serializer = new BinaryFormatter();                
327
328                         serializer.SurrogateSelector = null;
329                         mem.Position = 0;
330
331                         return serializer.Deserialize (mem);
332                 }
333         }
334         
335         internal class AsyncRequest
336         {
337                 internal IMessageSink ReplySink;
338                 internal IMessage MsgRequest;
339                 
340                 public AsyncRequest (IMessage msgRequest, IMessageSink replySink)
341                 {
342                         ReplySink = replySink;
343                         MsgRequest = msgRequest;
344                 }
345         }
346
347 }