2003-03-15 Lluis Sanchez Gual <lluis@ideary.com>
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Channels / CrossAppDomainChannel.cs
1 //
2 // System.Runtime.Remoting.Channels.CrossDomainChannel.cs
3 //
4 // Author: Patrik Torstensson (totte_mono@yahoo.com)
5 //
6 // 2003 (C) Copyright, Ximian, Inc.
7 //
8
9 using System.Collections;
10 using System.IO;\r
11 using System.Threading;\r
12 using System.Runtime.Remoting;\r
13 using System.Runtime.Remoting.Messaging;   \r
14 using System.Runtime.Remoting.Channels; \r
15 using System.Runtime.Remoting.Contexts; \r
16 using System.Runtime.Serialization;\r
17 using System.Runtime.Serialization.Formatters.Binary;\r
18
19 namespace System.Runtime.Remoting.Channels \r
20 {
21
22         // Holds the cross appdomain channel data (used to get/create the correct sink)
23         [Serializable]
24         internal class CrossAppDomainChannelData \r
25         {
26                 // TODO: Add context support
27                 private int _domainId;\r
28 \r
29                 internal CrossAppDomainChannelData(int domainId) \r
30                 {\r
31                         _domainId = domainId;\r
32                 }\r
33 \r
34                 internal int DomainID \r
35                 {  \r
36                         get { return _domainId; }\r
37                 }\r
38         }
39
40         // Responsible for marshalling objects between appdomains
41         [Serializable]\r
42         internal class CrossAppDomainChannel : IChannel, IChannelSender, IChannelReceiver \r
43         {
44                 private const String _strName = "MONOCAD";
45                 private const String _strBaseURI = "MONOCADURI";
46                 
47                 private static Object s_lock = new Object();
48 \r
49                 internal static void RegisterCrossAppDomainChannel() \r
50                 {\r
51                         lock (s_lock) \r
52                         {\r
53                                 // todo: make singleton\r
54                                 CrossAppDomainChannel monocad = new CrossAppDomainChannel();\r
55                                 ChannelServices.RegisterChannel ((IChannel) monocad);\r
56                         }\r
57                 }               \r
58 \r
59                 // IChannel implementation\r
60                 public virtual String ChannelName \r
61                 {\r
62                         get { return _strName; }\r
63                 }\r
64     \r
65                 public virtual int ChannelPriority \r
66                 {\r
67                         get { return 100; }\r
68                 }\r
69                 \r
70                 public String Parse(String url, out String objectURI) \r
71                 {\r
72                         objectURI = url;\r
73                         return null;\r
74                 }       \r
75 \r
76                 // IChannelReceiver\r
77                 public virtual Object ChannelData \r
78                 {\r
79                         get { return new CrossAppDomainChannelData(Thread.GetDomainID()); }\r
80                 }       \r
81                 \r
82                 public virtual String[] GetUrlsForUri(String objectURI) \r
83                 {\r
84                         throw new NotSupportedException("CrossAppdomain channel dont support UrlsForUri");\r
85                 }       \r
86                 \r
87                 // Dummies\r
88                 public virtual void StartListening(Object data) {}\r
89                 public virtual void StopListening(Object data) {}       \r
90 \r
91                 // IChannelSender\r
92                 public virtual IMessageSink CreateMessageSink(String url, Object data, out String uri) \r
93                 {\r
94                         uri = null;\r
95                         IMessageSink sink = null;\r
96             \r
97                         if (url == null && data != null) \r
98                         {\r
99                                 // Get the data and then get the sink\r
100                                 CrossAppDomainChannelData cadData = data as CrossAppDomainChannelData;\r
101                                 if (cadData != null) \r
102                                         // GetSink creates a new sink if we don't have any (use contexts here later)\r
103                                         sink = CrossAppDomainSink.GetSink(cadData.DomainID);\r
104                         } \r
105                         else \r
106                         {\r
107                                 if (url != null && data == null) \r
108                                 {\r
109                                         if (url.StartsWith(_strName)) \r
110                                         {\r
111                                                 throw new NotSupportedException("Can't create a named channel via crossappdomain");\r
112                                         }\r
113                                 }\r
114                         }\r
115 \r
116                         return sink;\r
117                 }\r
118 \r
119         }
120         \r
121         [MonoTODO("Handle domain unloading?")]\r
122         internal class CrossAppDomainSink : IMessageSink \r
123         {\r
124                 private static Hashtable s_sinks = new Hashtable();\r
125 \r
126                 private int _domainID;\r
127 \r
128                 internal CrossAppDomainSink(int domainID) \r
129                 {\r
130                         _domainID = domainID;\r
131                 }\r
132                 \r
133                 internal static CrossAppDomainSink GetSink(int domainID) \r
134                 {\r
135                         // Check if we have a sink for the current domainID\r
136                         // note, locking is not to bad here, very few class to GetSink\r
137                         lock (s_sinks.SyncRoot) \r
138                         {\r
139                                 if (s_sinks.ContainsKey(domainID)) \r
140                                         return (CrossAppDomainSink) s_sinks[domainID];\r
141                                 else \r
142                                 {\r
143                                         CrossAppDomainSink sink = new CrossAppDomainSink(domainID);\r
144                                         s_sinks[domainID] = sink;\r
145 \r
146                                         return sink;\r
147                                 }\r
148                         }\r
149                 }
150 \r
151                 public virtual IMessage SyncProcessMessage(IMessage msgRequest) \r
152                 {\r
153                         IMessage retMessage = null;\r
154 \r
155                         try \r
156                         {\r
157                                 // Time to transit into the "our" domain\r
158                                 byte [] arrResponse = null;\r
159                                 byte [] arrRequest = null; \r
160                                 \r
161                                 CADMethodReturnMessage cadMrm = null;\r
162                                 CADMethodCallMessage cadMsg;\r
163                                 \r
164                                 cadMsg = CADMethodCallMessage.Create (msgRequest);\r
165                                 if (null == cadMsg) {\r
166                                         // Serialize the request message\r
167                                         MemoryStream reqMsgStream = CADSerializer.SerializeMessage(msgRequest);\r
168                                         arrRequest = reqMsgStream.GetBuffer();\r
169                                 }\r
170 \r
171                                 Context currentContext = Thread.CurrentContext;
172                                 AppDomain currentDomain = AppDomain.InternalSetDomainByID ( _domainID );
173
174                                 try 
175                                 {
176                                         AppDomain.CurrentDomain.ProcessMessageInDomain (arrRequest, cadMsg, out arrResponse, out cadMrm);
177                                 }
178                                 catch (Exception e) 
179                                 {
180                                         IMessage errorMsg = new MethodResponse (e, new ErrorMessage());
181                                         arrResponse = CADSerializer.SerializeMessage (errorMsg).GetBuffer(); \r
182                                 }   
183                                 finally 
184                                 {
185                                         AppDomain.InternalSetDomain (currentDomain);
186                                         AppDomain.InternalSetContext (currentContext);
187                                 }\r
188                                 \r
189                                 if (null != arrResponse) {\r
190                                         // Time to deserialize the message\r
191                                         MemoryStream respMsgStream = new MemoryStream(arrResponse);\r
192 \r
193                                         // Deserialize the response message\r
194                                         retMessage = CADSerializer.DeserializeMessage(respMsgStream, msgRequest as IMethodCallMessage);\r
195                                 } else\r
196                                         retMessage = new MethodResponse (msgRequest as IMethodCallMessage, cadMrm);\r
197                         }\r
198                         catch (Exception e) \r
199                         {\r
200                                 try\r
201                                 {\r
202                                         Console.WriteLine("Exception in base domain");
203                                         retMessage = new ReturnMessage (e, msgRequest as IMethodCallMessage);
204                                 }\r
205                                 catch (Exception)\r
206                                 {\r
207                                         // this is just to be sure\r
208                                 }\r
209                         }\r
210 \r
211                         return retMessage;\r
212                 }
213
214                 public virtual IMessageCtrl AsyncProcessMessage(IMessage reqMsg, IMessageSink replySink) \r
215                 {\r
216                         throw new NotSupportedException();\r
217                 }
218                 \r
219                 public IMessageSink NextSink { get { return null; } }\r
220         }
221
222         internal class CADSerializer 
223         {
224                 internal static IMessage DeserializeMessage(MemoryStream mem, IMethodCallMessage msg)\r
225                 {\r
226                         BinaryFormatter serializer = new BinaryFormatter();                \r
227 \r
228                         serializer.SurrogateSelector = null;\r
229                         mem.Position = 0;\r
230 \r
231                         if (msg == null)\r
232                                 return (IMessage) serializer.Deserialize(mem, null);\r
233                         else\r
234                                 return (IMessage) serializer.DeserializeMethodResponse(mem, null, msg);\r
235                 }\r
236                 \r
237                 internal static MemoryStream SerializeMessage(IMessage msg)\r
238                 {\r
239                         MemoryStream mem = new MemoryStream ();\r
240                         BinaryFormatter serializer = new BinaryFormatter ();                \r
241 \r
242                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();\r
243                         serializer.Serialize (mem, msg);\r
244 \r
245                         mem.Position = 0;\r
246 \r
247                         return mem;\r
248                 }
249
250                 internal static MemoryStream SerializeObject(object obj)\r
251                 {\r
252                         MemoryStream mem = new MemoryStream ();\r
253                         BinaryFormatter serializer = new BinaryFormatter ();                \r
254 \r
255                         serializer.SurrogateSelector = new RemotingSurrogateSelector ();\r
256                         serializer.Serialize (mem, obj);\r
257 \r
258                         mem.Position = 0;\r
259 \r
260                         return mem;\r
261                 }
262 \r
263                 internal static object DeserializeObject(MemoryStream mem)\r
264                 {\r
265                         BinaryFormatter serializer = new BinaryFormatter();                \r
266 \r
267                         serializer.SurrogateSelector = null;\r
268                         mem.Position = 0;\r
269 \r
270                         return serializer.Deserialize (mem);\r
271                 }
272         }
273 }