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