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