* Mono.Posix.dll.sources: Rename Mono.Posix to Mono.Unix.
[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             \r
129                         if (data != null) \r
130                         {\r
131                                 // Get the data and then get the sink\r
132                                 CrossAppDomainData cadData = data as CrossAppDomainData;\r
133                                 if (cadData != null && cadData.ProcessID == RemotingConfiguration.ProcessId)\r
134                                         // GetSink creates a new sink if we don't have any (use contexts here later)\r
135                                         return CrossAppDomainSink.GetSink(cadData.DomainID);\r
136                         } \r
137                         if (url != null && url.StartsWith(_strName)) \r
138                                 throw new NotSupportedException("Can't create a named channel via crossappdomain");\r
139 \r
140                         return null;\r
141                 }
142         }
143         \r
144         [MonoTODO("Handle domain unloading?")]\r
145         internal class CrossAppDomainSink : IMessageSink \r
146         {\r
147                 private static Hashtable s_sinks = new Hashtable();\r
148
149                 private static MethodInfo processMessageMethod =
150                         typeof (CrossAppDomainSink).GetMethod ("ProcessMessageInDomain", BindingFlags.NonPublic|BindingFlags.Static);
151
152 \r
153                 private int _domainID;\r
154 \r
155                 internal CrossAppDomainSink(int domainID) \r
156                 {\r
157                         _domainID = domainID;\r
158                 }\r
159                 \r
160                 internal static CrossAppDomainSink GetSink(int domainID) \r
161                 {\r
162                         // Check if we have a sink for the current domainID\r
163                         // note, locking is not to bad here, very few class to GetSink\r
164                         lock (s_sinks.SyncRoot) \r
165                         {\r
166                                 if (s_sinks.ContainsKey(domainID)) \r
167                                         return (CrossAppDomainSink) s_sinks[domainID];\r
168                                 else \r
169                                 {\r
170                                         CrossAppDomainSink sink = new CrossAppDomainSink(domainID);\r
171                                         s_sinks[domainID] = sink;\r
172 \r
173                                         return sink;\r
174                                 }\r
175                         }\r
176                 }\r
177                 \r
178                 internal int TargetDomainId {\r
179                         get { return _domainID; }\r
180                 }
181
182                 private struct ProcessMessageRes {
183                         public byte[] arrResponse;
184                         public CADMethodReturnMessage cadMrm;
185                 }
186
187                 private static ProcessMessageRes ProcessMessageInDomain (
188                         byte[] arrRequest,
189                         CADMethodCallMessage cadMsg)
190             {
191                         ProcessMessageRes res = new ProcessMessageRes ();
192
193                         try 
194                         {
195                                 AppDomain.CurrentDomain.ProcessMessageInDomain (arrRequest, cadMsg, out res.arrResponse, out res.cadMrm);
196                         }
197                         catch (Exception e) 
198                         {
199                                 IMessage errorMsg = new MethodResponse (e, new ErrorMessage());
200                                 res.arrResponse = CADSerializer.SerializeMessage (errorMsg).GetBuffer(); \r
201                         }
202                         return res;
203                 }
204 \r
205                 public virtual IMessage SyncProcessMessage(IMessage msgRequest) \r
206                 {\r
207                         IMessage retMessage = null;\r
208 \r
209                         try \r
210                         {\r
211                                 // Time to transit into the "our" domain\r
212                                 byte [] arrResponse = null;\r
213                                 byte [] arrRequest = null; \r
214                                 \r
215                                 CADMethodReturnMessage cadMrm = null;\r
216                                 CADMethodCallMessage cadMsg;\r
217                                 \r
218                                 cadMsg = CADMethodCallMessage.Create (msgRequest);\r
219                                 if (null == cadMsg) {\r
220                                         // Serialize the request message\r
221                                         MemoryStream reqMsgStream = CADSerializer.SerializeMessage(msgRequest);\r
222                                         arrRequest = reqMsgStream.GetBuffer();\r
223                                 }\r
224 \r
225                                 object threadStatus = Thread.ResetDataStoreStatus ();\r
226                                 Context currentContext = Thread.CurrentContext;
227
228                                 try {
229                                         // InternalInvoke can't handle out arguments, this is why
230                                         // we return the results in a structure
231                                         ProcessMessageRes res = (ProcessMessageRes)AppDomain.InvokeInDomainByID (_domainID, processMessageMethod, null, new object [] { arrRequest, cadMsg });
232                                         arrResponse = res.arrResponse;
233                                         cadMrm = res.cadMrm;
234                                 }
235                                 finally {
236                                         AppDomain.InternalSetContext (currentContext);
237                                         Thread.RestoreDataStoreStatus (threadStatus);
238                                 }                                       
239
240                                 \r
241                                 if (null != arrResponse) {\r
242                                         // Time to deserialize the message\r
243                                         MemoryStream respMsgStream = new MemoryStream(arrResponse);\r
244 \r
245                                         // Deserialize the response message\r
246                                         retMessage = CADSerializer.DeserializeMessage(respMsgStream, msgRequest as IMethodCallMessage);\r
247                                 } else\r
248                                         retMessage = new MethodResponse (msgRequest as IMethodCallMessage, cadMrm);\r
249                         }\r
250                         catch (Exception e) \r
251                         {\r
252                                 try\r
253                                 {\r
254                                         retMessage = new ReturnMessage (e, msgRequest as IMethodCallMessage);
255                                 }\r
256                                 catch (Exception)\r
257                                 {\r
258                                         // this is just to be sure\r
259                                 }\r
260                         }\r
261 \r
262                 return retMessage;\r
263                 }
264
265                 public virtual IMessageCtrl AsyncProcessMessage (IMessage reqMsg, IMessageSink replySink) \r
266                 {\r
267                         AsyncRequest req = new AsyncRequest (reqMsg, replySink);\r
268                         ThreadPool.QueueUserWorkItem (new WaitCallback (SendAsyncMessage), req);\r
269                         return null;\r
270                 }
271                 \r
272                 public void SendAsyncMessage (object data)\r
273                 {\r
274                         AsyncRequest req = (AsyncRequest)data;\r
275                         IMessage response = SyncProcessMessage (req.MsgRequest);\r
276                         req.ReplySink.SyncProcessMessage (response);\r
277                 }\r
278                 \r
279                 public IMessageSink NextSink { get { return null; } }\r
280         }
281
282         internal class CADSerializer 
283         {
284                 internal static IMessage DeserializeMessage(MemoryStream mem, IMethodCallMessage msg)\r
285                 {\r
286                         BinaryFormatter serializer = new BinaryFormatter();                \r
287 \r
288                         serializer.SurrogateSelector = null;\r
289                         mem.Position = 0;\r
290 \r
291                         if (msg == null)\r
292                                 return (IMessage) serializer.Deserialize(mem, null);\r
293                         else\r
294                                 return (IMessage) serializer.DeserializeMethodResponse(mem, null, msg);\r
295                 }\r
296                 \r
297                 internal static MemoryStream SerializeMessage(IMessage msg)\r
298                 {\r
299                         MemoryStream mem = new MemoryStream ();\r
300                         BinaryFormatter serializer = new BinaryFormatter ();                \r
301 \r
302                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();\r
303                         serializer.Serialize (mem, msg);\r
304 \r
305                         mem.Position = 0;\r
306 \r
307                         return mem;\r
308                 }
309
310                 internal static MemoryStream SerializeObject(object obj)\r
311                 {\r
312                         MemoryStream mem = new MemoryStream ();\r
313                         BinaryFormatter serializer = new BinaryFormatter ();                \r
314 \r
315                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();\r
316                         serializer.Serialize (mem, obj);\r
317 \r
318                         mem.Position = 0;\r
319 \r
320                         return mem;\r
321                 }
322 \r
323                 internal static object DeserializeObject(MemoryStream mem)\r
324                 {\r
325                         BinaryFormatter serializer = new BinaryFormatter();                \r
326 \r
327                         serializer.SurrogateSelector = null;\r
328                         mem.Position = 0;\r
329 \r
330                         return serializer.Deserialize (mem);\r
331                 }
332         }\r
333         \r
334         internal class AsyncRequest\r
335         {\r
336                 internal IMessageSink ReplySink;\r
337                 internal IMessage MsgRequest;\r
338                 \r
339                 public AsyncRequest (IMessage msgRequest, IMessageSink replySink)\r
340                 {\r
341                         ReplySink = replySink;\r
342                         MsgRequest = msgRequest;\r
343                 }\r
344         }
345
346 }