aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Communications/RestClient.cs
diff options
context:
space:
mode:
authorJeff Ames2008-05-02 03:34:06 +0000
committerJeff Ames2008-05-02 03:34:06 +0000
commit058191e6cc45d546058ba0a0ae18054f7ada3bf6 (patch)
treeaf81714bf91d323b0c2084b8ac94fd9ece381ed6 /OpenSim/Framework/Communications/RestClient.cs
parentMinimize binary size and external dependencies in OS X LaunchSLClient.app cre... (diff)
downloadopensim-SC_OLD-058191e6cc45d546058ba0a0ae18054f7ada3bf6.zip
opensim-SC_OLD-058191e6cc45d546058ba0a0ae18054f7ada3bf6.tar.gz
opensim-SC_OLD-058191e6cc45d546058ba0a0ae18054f7ada3bf6.tar.bz2
opensim-SC_OLD-058191e6cc45d546058ba0a0ae18054f7ada3bf6.tar.xz
Update svn properties.
Diffstat (limited to 'OpenSim/Framework/Communications/RestClient.cs')
-rw-r--r--OpenSim/Framework/Communications/RestClient.cs734
1 files changed, 367 insertions, 367 deletions
diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs
index b0b7b1e..d0ac833 100644
--- a/OpenSim/Framework/Communications/RestClient.cs
+++ b/OpenSim/Framework/Communications/RestClient.cs
@@ -1,368 +1,368 @@
1using System; 1using System;
2using System.Collections.Generic; 2using System.Collections.Generic;
3using System.IO; 3using System.IO;
4using System.Net; 4using System.Net;
5using System.Reflection; 5using System.Reflection;
6using System.Text; 6using System.Text;
7using System.Threading; 7using System.Threading;
8using System.Web; 8using System.Web;
9using log4net; 9using log4net;
10 10
11namespace OpenSim.Framework.Communications 11namespace OpenSim.Framework.Communications
12{ 12{
13 /// <summary> 13 /// <summary>
14 /// Implementation of a generic REST client 14 /// Implementation of a generic REST client
15 /// </summary> 15 /// </summary>
16 /// <remarks> 16 /// <remarks>
17 /// This class is a generic implementation of a REST (Representational State Transfer) web service. This 17 /// This class is a generic implementation of a REST (Representational State Transfer) web service. This
18 /// class is designed to execute both synchroneously and asynchroneously. 18 /// class is designed to execute both synchroneously and asynchroneously.
19 /// 19 ///
20 /// Internally the implementation works as a two stage asynchroneous web-client. 20 /// Internally the implementation works as a two stage asynchroneous web-client.
21 /// When the request is initiated, RestClient will query asynchroneously for for a web-response, 21 /// When the request is initiated, RestClient will query asynchroneously for for a web-response,
22 /// sleeping until the initial response is returned by the server. Once the initial response is retrieved 22 /// sleeping until the initial response is returned by the server. Once the initial response is retrieved
23 /// the second stage of asynchroneous requests will be triggered, in an attempt to read of the response 23 /// the second stage of asynchroneous requests will be triggered, in an attempt to read of the response
24 /// object into a memorystream as a sequence of asynchroneous reads. 24 /// object into a memorystream as a sequence of asynchroneous reads.
25 /// 25 ///
26 /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing 26 /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing
27 /// other threads to execute, while it waits for a response from the web-service. RestClient it self, can be 27 /// other threads to execute, while it waits for a response from the web-service. RestClient it self, can be
28 /// invoked by the caller in either synchroneous mode or asynchroneous mode. 28 /// invoked by the caller in either synchroneous mode or asynchroneous mode.
29 /// </remarks> 29 /// </remarks>
30 public class RestClient 30 public class RestClient
31 { 31 {
32 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 32 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
33 33
34 private string realuri; 34 private string realuri;
35 35
36 #region member variables 36 #region member variables
37 37
38 /// <summary> 38 /// <summary>
39 /// The base Uri of the web-service e.g. http://www.google.com 39 /// The base Uri of the web-service e.g. http://www.google.com
40 /// </summary> 40 /// </summary>
41 private string _url; 41 private string _url;
42 42
43 /// <summary> 43 /// <summary>
44 /// Path elements of the query 44 /// Path elements of the query
45 /// </summary> 45 /// </summary>
46 private List<string> _pathElements = new List<string>(); 46 private List<string> _pathElements = new List<string>();
47 47
48 /// <summary> 48 /// <summary>
49 /// Parameter elements of the query, e.g. min=34 49 /// Parameter elements of the query, e.g. min=34
50 /// </summary> 50 /// </summary>
51 private Dictionary<string, string> _parameterElements = new Dictionary<string, string>(); 51 private Dictionary<string, string> _parameterElements = new Dictionary<string, string>();
52 52
53 /// <summary> 53 /// <summary>
54 /// Request method. E.g. GET, POST, PUT or DELETE 54 /// Request method. E.g. GET, POST, PUT or DELETE
55 /// </summary> 55 /// </summary>
56 private string _method; 56 private string _method;
57 57
58 /// <summary> 58 /// <summary>
59 /// Temporary buffer used to store bytes temporarily as they come in from the server 59 /// Temporary buffer used to store bytes temporarily as they come in from the server
60 /// </summary> 60 /// </summary>
61 private byte[] _readbuf; 61 private byte[] _readbuf;
62 62
63 /// <summary> 63 /// <summary>
64 /// MemoryStream representing the resultiong resource 64 /// MemoryStream representing the resultiong resource
65 /// </summary> 65 /// </summary>
66 private Stream _resource; 66 private Stream _resource;
67 67
68 /// <summary> 68 /// <summary>
69 /// WebRequest object, held as a member variable 69 /// WebRequest object, held as a member variable
70 /// </summary> 70 /// </summary>
71 private HttpWebRequest _request; 71 private HttpWebRequest _request;
72 72
73 /// <summary> 73 /// <summary>
74 /// WebResponse object, held as a member variable, so we can close it 74 /// WebResponse object, held as a member variable, so we can close it
75 /// </summary> 75 /// </summary>
76 private HttpWebResponse _response; 76 private HttpWebResponse _response;
77 77
78 /// <summary> 78 /// <summary>
79 /// This flag will help block the main synchroneous method, in case we run in synchroneous mode 79 /// This flag will help block the main synchroneous method, in case we run in synchroneous mode
80 /// </summary> 80 /// </summary>
81 public static ManualResetEvent _allDone = new ManualResetEvent(false); 81 public static ManualResetEvent _allDone = new ManualResetEvent(false);
82 82
83 /// <summary> 83 /// <summary>
84 /// Default time out period 84 /// Default time out period
85 /// </summary> 85 /// </summary>
86 private const int DefaultTimeout = 10*1000; // 10 seconds timeout 86 private const int DefaultTimeout = 10*1000; // 10 seconds timeout
87 87
88 /// <summary> 88 /// <summary>
89 /// Default Buffer size of a block requested from the web-server 89 /// Default Buffer size of a block requested from the web-server
90 /// </summary> 90 /// </summary>
91 private const int BufferSize = 4096; // Read blocks of 4 KB. 91 private const int BufferSize = 4096; // Read blocks of 4 KB.
92 92
93 93
94 /// <summary> 94 /// <summary>
95 /// if an exception occours during async processing, we need to save it, so it can be 95 /// if an exception occours during async processing, we need to save it, so it can be
96 /// rethrown on the primary thread; 96 /// rethrown on the primary thread;
97 /// </summary> 97 /// </summary>
98 private Exception _asyncException; 98 private Exception _asyncException;
99 99
100 #endregion member variables 100 #endregion member variables
101 101
102 #region constructors 102 #region constructors
103 103
104 /// <summary> 104 /// <summary>
105 /// Instantiate a new RestClient 105 /// Instantiate a new RestClient
106 /// </summary> 106 /// </summary>
107 /// <param name="url">Web-service to query, e.g. http://osgrid.org:8003</param> 107 /// <param name="url">Web-service to query, e.g. http://osgrid.org:8003</param>
108 public RestClient(string url) 108 public RestClient(string url)
109 { 109 {
110 _url = url; 110 _url = url;
111 _readbuf = new byte[BufferSize]; 111 _readbuf = new byte[BufferSize];
112 _resource = new MemoryStream(); 112 _resource = new MemoryStream();
113 _request = null; 113 _request = null;
114 _response = null; 114 _response = null;
115 _lock = new object(); 115 _lock = new object();
116 } 116 }
117 117
118 private object _lock; 118 private object _lock;
119 119
120 #endregion constructors 120 #endregion constructors
121 121
122 /// <summary> 122 /// <summary>
123 /// Add a path element to the query, e.g. assets 123 /// Add a path element to the query, e.g. assets
124 /// </summary> 124 /// </summary>
125 /// <param name="element">path entry</param> 125 /// <param name="element">path entry</param>
126 public void AddResourcePath(string element) 126 public void AddResourcePath(string element)
127 { 127 {
128 if (isSlashed(element)) 128 if (isSlashed(element))
129 _pathElements.Add(element.Substring(0, element.Length - 1)); 129 _pathElements.Add(element.Substring(0, element.Length - 1));
130 else 130 else
131 _pathElements.Add(element); 131 _pathElements.Add(element);
132 } 132 }
133 133
134 /// <summary> 134 /// <summary>
135 /// Add a query parameter to the Url 135 /// Add a query parameter to the Url
136 /// </summary> 136 /// </summary>
137 /// <param name="name">Name of the parameter, e.g. min</param> 137 /// <param name="name">Name of the parameter, e.g. min</param>
138 /// <param name="value">Value of the parameter, e.g. 42</param> 138 /// <param name="value">Value of the parameter, e.g. 42</param>
139 public void AddQueryParameter(string name, string value) 139 public void AddQueryParameter(string name, string value)
140 { 140 {
141 _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value)); 141 _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value));
142 } 142 }
143 143
144 /// <summary> 144 /// <summary>
145 /// Add a query parameter to the Url 145 /// Add a query parameter to the Url
146 /// </summary> 146 /// </summary>
147 /// <param name="name">Name of the parameter, e.g. min</param> 147 /// <param name="name">Name of the parameter, e.g. min</param>
148 public void AddQueryParameter(string name) 148 public void AddQueryParameter(string name)
149 { 149 {
150 _parameterElements.Add(HttpUtility.UrlEncode(name), null); 150 _parameterElements.Add(HttpUtility.UrlEncode(name), null);
151 } 151 }
152 152
153 /// <summary> 153 /// <summary>
154 /// Web-Request method, e.g. GET, PUT, POST, DELETE 154 /// Web-Request method, e.g. GET, PUT, POST, DELETE
155 /// </summary> 155 /// </summary>
156 public string RequestMethod 156 public string RequestMethod
157 { 157 {
158 get { return _method; } 158 get { return _method; }
159 set { _method = value; } 159 set { _method = value; }
160 } 160 }
161 161
162 /// <summary> 162 /// <summary>
163 /// True if string contains a trailing slash '/' 163 /// True if string contains a trailing slash '/'
164 /// </summary> 164 /// </summary>
165 /// <param name="s">string to be examined</param> 165 /// <param name="s">string to be examined</param>
166 /// <returns>true if slash is present</returns> 166 /// <returns>true if slash is present</returns>
167 private static bool isSlashed(string s) 167 private static bool isSlashed(string s)
168 { 168 {
169 return s.Substring(s.Length - 1, 1) == "/"; 169 return s.Substring(s.Length - 1, 1) == "/";
170 } 170 }
171 171
172 /// <summary> 172 /// <summary>
173 /// Build a Uri based on the initial Url, path elements and parameters 173 /// Build a Uri based on the initial Url, path elements and parameters
174 /// </summary> 174 /// </summary>
175 /// <returns>fully constructed Uri</returns> 175 /// <returns>fully constructed Uri</returns>
176 private Uri buildUri() 176 private Uri buildUri()
177 { 177 {
178 StringBuilder sb = new StringBuilder(); 178 StringBuilder sb = new StringBuilder();
179 sb.Append(_url); 179 sb.Append(_url);
180 180
181 foreach (string e in _pathElements) 181 foreach (string e in _pathElements)
182 { 182 {
183 sb.Append("/"); 183 sb.Append("/");
184 sb.Append(e); 184 sb.Append(e);
185 } 185 }
186 186
187 bool firstElement = true; 187 bool firstElement = true;
188 foreach (KeyValuePair<string, string> kv in _parameterElements) 188 foreach (KeyValuePair<string, string> kv in _parameterElements)
189 { 189 {
190 if (firstElement) 190 if (firstElement)
191 { 191 {
192 sb.Append("?"); 192 sb.Append("?");
193 firstElement = false; 193 firstElement = false;
194 } 194 }
195 else 195 else
196 sb.Append("&"); 196 sb.Append("&");
197 197
198 sb.Append(kv.Key); 198 sb.Append(kv.Key);
199 if (!string.IsNullOrEmpty(kv.Value)) 199 if (!string.IsNullOrEmpty(kv.Value))
200 { 200 {
201 sb.Append("="); 201 sb.Append("=");
202 sb.Append(kv.Value); 202 sb.Append(kv.Value);
203 } 203 }
204 } 204 }
205 realuri = sb.ToString(); 205 realuri = sb.ToString();
206 //m_log.InfoFormat("[REST CLIENT]: RestURL: {0}", realuri); 206 //m_log.InfoFormat("[REST CLIENT]: RestURL: {0}", realuri);
207 return new Uri(sb.ToString()); 207 return new Uri(sb.ToString());
208 } 208 }
209 209
210 #region Async communications with server 210 #region Async communications with server
211 211
212 /// <summary> 212 /// <summary>
213 /// Async method, invoked when a block of data has been received from the service 213 /// Async method, invoked when a block of data has been received from the service
214 /// </summary> 214 /// </summary>
215 /// <param name="ar"></param> 215 /// <param name="ar"></param>
216 private void StreamIsReadyDelegate(IAsyncResult ar) 216 private void StreamIsReadyDelegate(IAsyncResult ar)
217 { 217 {
218 try 218 try
219 { 219 {
220 Stream s = (Stream) ar.AsyncState; 220 Stream s = (Stream) ar.AsyncState;
221 int read = s.EndRead(ar); 221 int read = s.EndRead(ar);
222 222
223 if (read > 0) 223 if (read > 0)
224 { 224 {
225 _resource.Write(_readbuf, 0, read); 225 _resource.Write(_readbuf, 0, read);
226 IAsyncResult asynchronousResult = 226 IAsyncResult asynchronousResult =
227 s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s); 227 s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s);
228 228
229 // TODO! Implement timeout, without killing the server 229 // TODO! Implement timeout, without killing the server
230 //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); 230 //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
231 } 231 }
232 else 232 else
233 { 233 {
234 s.Close(); 234 s.Close();
235 _allDone.Set(); 235 _allDone.Set();
236 } 236 }
237 } 237 }
238 catch (Exception e) 238 catch (Exception e)
239 { 239 {
240 _allDone.Set(); 240 _allDone.Set();
241 _asyncException = e; 241 _asyncException = e;
242 } 242 }
243 } 243 }
244 244
245 #endregion Async communications with server 245 #endregion Async communications with server
246 246
247 /// <summary> 247 /// <summary>
248 /// Perform synchroneous request 248 /// Perform synchroneous request
249 /// </summary> 249 /// </summary>
250 public Stream Request() 250 public Stream Request()
251 { 251 {
252 lock (_lock) 252 lock (_lock)
253 { 253 {
254 _request = (HttpWebRequest) WebRequest.Create(buildUri()); 254 _request = (HttpWebRequest) WebRequest.Create(buildUri());
255 _request.KeepAlive = false; 255 _request.KeepAlive = false;
256 _request.ContentType = "application/xml"; 256 _request.ContentType = "application/xml";
257 _request.Timeout = 200000; 257 _request.Timeout = 200000;
258 _asyncException = null; 258 _asyncException = null;
259 259
260// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); 260// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request);
261 _response = (HttpWebResponse) _request.GetResponse(); 261 _response = (HttpWebResponse) _request.GetResponse();
262 Stream src = _response.GetResponseStream(); 262 Stream src = _response.GetResponseStream();
263 int length = src.Read(_readbuf, 0, BufferSize); 263 int length = src.Read(_readbuf, 0, BufferSize);
264 while (length > 0) 264 while (length > 0)
265 { 265 {
266 _resource.Write(_readbuf, 0, length); 266 _resource.Write(_readbuf, 0, length);
267 length = src.Read(_readbuf, 0, BufferSize); 267 length = src.Read(_readbuf, 0, BufferSize);
268 } 268 }
269 269
270 270
271 // TODO! Implement timeout, without killing the server 271 // TODO! Implement timeout, without killing the server
272 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted 272 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
273 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); 273 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
274 274
275// _allDone.WaitOne(); 275// _allDone.WaitOne();
276 if (_response != null) 276 if (_response != null)
277 _response.Close(); 277 _response.Close();
278 if (_asyncException != null) 278 if (_asyncException != null)
279 throw _asyncException; 279 throw _asyncException;
280 280
281 if (_resource != null) 281 if (_resource != null)
282 { 282 {
283 _resource.Flush(); 283 _resource.Flush();
284 _resource.Seek(0, SeekOrigin.Begin); 284 _resource.Seek(0, SeekOrigin.Begin);
285 } 285 }
286 286
287 return _resource; 287 return _resource;
288 } 288 }
289 } 289 }
290 290
291 public Stream Request(Stream src) 291 public Stream Request(Stream src)
292 { 292 {
293 _request = (HttpWebRequest) WebRequest.Create(buildUri()); 293 _request = (HttpWebRequest) WebRequest.Create(buildUri());
294 _request.KeepAlive = false; 294 _request.KeepAlive = false;
295 _request.ContentType = "application/xml"; 295 _request.ContentType = "application/xml";
296 _request.Timeout = 900000; 296 _request.Timeout = 900000;
297 _request.Method = RequestMethod; 297 _request.Method = RequestMethod;
298 _asyncException = null; 298 _asyncException = null;
299 _request.ContentLength = src.Length; 299 _request.ContentLength = src.Length;
300 300
301 m_log.InfoFormat("[REST]: Request Length {0}", _request.ContentLength); 301 m_log.InfoFormat("[REST]: Request Length {0}", _request.ContentLength);
302 m_log.InfoFormat("[REST]: Sending Web Request {0}", buildUri()); 302 m_log.InfoFormat("[REST]: Sending Web Request {0}", buildUri());
303 src.Seek(0, SeekOrigin.Begin); 303 src.Seek(0, SeekOrigin.Begin);
304 m_log.Info("[REST]: Seek is ok"); 304 m_log.Info("[REST]: Seek is ok");
305 Stream dst = _request.GetRequestStream(); 305 Stream dst = _request.GetRequestStream();
306 m_log.Info("[REST]: GetRequestStream is ok"); 306 m_log.Info("[REST]: GetRequestStream is ok");
307 307
308 byte[] buf = new byte[1024]; 308 byte[] buf = new byte[1024];
309 int length = src.Read(buf, 0, 1024); 309 int length = src.Read(buf, 0, 1024);
310 m_log.Info("[REST]: First Read is ok"); 310 m_log.Info("[REST]: First Read is ok");
311 while (length > 0) 311 while (length > 0)
312 { 312 {
313 dst.Write(buf, 0, length); 313 dst.Write(buf, 0, length);
314 length = src.Read(buf, 0, 1024); 314 length = src.Read(buf, 0, 1024);
315 } 315 }
316 316
317 _response = (HttpWebResponse) _request.GetResponse(); 317 _response = (HttpWebResponse) _request.GetResponse();
318 318
319// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); 319// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request);
320 320
321 // TODO! Implement timeout, without killing the server 321 // TODO! Implement timeout, without killing the server
322 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted 322 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
323 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); 323 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
324 324
325 return null; 325 return null;
326 } 326 }
327 327
328 #region Async Invocation 328 #region Async Invocation
329 329
330 public IAsyncResult BeginRequest(AsyncCallback callback, object state) 330 public IAsyncResult BeginRequest(AsyncCallback callback, object state)
331 { 331 {
332 /// <summary> 332 /// <summary>
333 /// In case, we are invoked asynchroneously this object will keep track of the state 333 /// In case, we are invoked asynchroneously this object will keep track of the state
334 /// </summary> 334 /// </summary>
335 AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state); 335 AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
336 ThreadPool.QueueUserWorkItem(RequestHelper, ar); 336 ThreadPool.QueueUserWorkItem(RequestHelper, ar);
337 return ar; 337 return ar;
338 } 338 }
339 339
340 public Stream EndRequest(IAsyncResult asyncResult) 340 public Stream EndRequest(IAsyncResult asyncResult)
341 { 341 {
342 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult; 342 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
343 343
344 // Wait for operation to complete, then return result or 344 // Wait for operation to complete, then return result or
345 // throw exception 345 // throw exception
346 return ar.EndInvoke(); 346 return ar.EndInvoke();
347 } 347 }
348 348
349 private void RequestHelper(Object asyncResult) 349 private void RequestHelper(Object asyncResult)
350 { 350 {
351 // We know that it's really an AsyncResult<DateTime> object 351 // We know that it's really an AsyncResult<DateTime> object
352 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult; 352 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
353 try 353 try
354 { 354 {
355 // Perform the operation; if sucessful set the result 355 // Perform the operation; if sucessful set the result
356 Stream s = Request(); 356 Stream s = Request();
357 ar.SetAsCompleted(s, false); 357 ar.SetAsCompleted(s, false);
358 } 358 }
359 catch (Exception e) 359 catch (Exception e)
360 { 360 {
361 // If operation fails, set the exception 361 // If operation fails, set the exception
362 ar.HandleException(e, false); 362 ar.HandleException(e, false);
363 } 363 }
364 } 364 }
365 365
366 #endregion Async Invocation 366 #endregion Async Invocation
367 } 367 }
368} \ No newline at end of file 368} \ No newline at end of file