aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs465
1 files changed, 465 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
new file mode 100644
index 0000000..8d4e561
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
@@ -0,0 +1,465 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Threading;
33using log4net;
34using Nini.Config;
35using Mono.Addins;
36using OpenMetaverse;
37using OpenSim.Framework;
38using OpenSim.Framework.Servers;
39using OpenSim.Framework.Servers.HttpServer;
40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes;
42using OpenSim.Framework.Capabilities;
43using OpenSim.Services.Interfaces;
44using Caps = OpenSim.Framework.Capabilities.Caps;
45using OpenSim.Capabilities.Handlers;
46using OpenSim.Framework.Monitoring;
47
48using OpenMetaverse.StructuredData;
49
50namespace OpenSim.Region.ClientStack.Linden
51{
52 /// <summary>
53 /// This module implements both WebFetchInventoryDescendents and FetchInventoryDescendents2 capabilities.
54 /// </summary>
55 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")]
56 public class WebFetchInvDescModule : INonSharedRegionModule
57 {
58 class aPollRequest
59 {
60 public PollServiceInventoryEventArgs thepoll;
61 public UUID reqID;
62 public Hashtable request;
63 public ScenePresence presence;
64 public List<UUID> folders;
65 }
66
67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
68
69 /// <summary>
70 /// Control whether requests will be processed asynchronously.
71 /// </summary>
72 /// <remarks>
73 /// Defaults to true. Can currently not be changed once a region has been added to the module.
74 /// </remarks>
75 public bool ProcessQueuedRequestsAsync { get; private set; }
76
77 /// <summary>
78 /// Number of inventory requests processed by this module.
79 /// </summary>
80 /// <remarks>
81 /// It's the PollServiceRequestManager that actually sends completed requests back to the requester.
82 /// </remarks>
83 public static int ProcessedRequestsCount { get; set; }
84
85 private static Stat s_queuedRequestsStat;
86 private static Stat s_processedRequestsStat;
87
88 public Scene Scene { get; private set; }
89
90 private IInventoryService m_InventoryService;
91 private ILibraryService m_LibraryService;
92
93 private bool m_Enabled;
94
95 private string m_fetchInventoryDescendents2Url;
96// private string m_webFetchInventoryDescendentsUrl;
97
98 private static FetchInvDescHandler m_webFetchHandler;
99
100 private static Thread[] m_workerThreads = null;
101
102 private static OpenSim.Framework.BlockingQueue<aPollRequest> m_queue =
103 new OpenSim.Framework.BlockingQueue<aPollRequest>();
104
105 private static int m_NumberScenes = 0;
106
107 #region ISharedRegionModule Members
108
109 public WebFetchInvDescModule() : this(true) {}
110
111 public WebFetchInvDescModule(bool processQueuedResultsAsync)
112 {
113 ProcessQueuedRequestsAsync = processQueuedResultsAsync;
114 }
115
116 public void Initialise(IConfigSource source)
117 {
118 IConfig config = source.Configs["ClientStack.LindenCaps"];
119 if (config == null)
120 return;
121
122 m_fetchInventoryDescendents2Url = config.GetString("Cap_FetchInventoryDescendents2", string.Empty);
123// m_webFetchInventoryDescendentsUrl = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty);
124
125// if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty)
126 if (m_fetchInventoryDescendents2Url != string.Empty)
127 {
128 m_Enabled = true;
129 }
130 }
131
132 public void AddRegion(Scene s)
133 {
134 if (!m_Enabled)
135 return;
136
137 Scene = s;
138 }
139
140 public void RemoveRegion(Scene s)
141 {
142 if (!m_Enabled)
143 return;
144
145 Scene.EventManager.OnRegisterCaps -= RegisterCaps;
146
147 StatsManager.DeregisterStat(s_processedRequestsStat);
148 StatsManager.DeregisterStat(s_queuedRequestsStat);
149
150 m_NumberScenes--;
151 Scene = null;
152 }
153
154 public void RegionLoaded(Scene s)
155 {
156 if (!m_Enabled)
157 return;
158
159 if (s_processedRequestsStat == null)
160 s_processedRequestsStat =
161 new Stat(
162 "ProcessedFetchInventoryRequests",
163 "Number of processed fetch inventory requests",
164 "These have not necessarily yet been dispatched back to the requester.",
165 "",
166 "inventory",
167 "httpfetch",
168 StatType.Pull,
169 MeasuresOfInterest.AverageChangeOverTime,
170 stat => { stat.Value = ProcessedRequestsCount; },
171 StatVerbosity.Debug);
172
173 if (s_queuedRequestsStat == null)
174 s_queuedRequestsStat =
175 new Stat(
176 "QueuedFetchInventoryRequests",
177 "Number of fetch inventory requests queued for processing",
178 "",
179 "",
180 "inventory",
181 "httpfetch",
182 StatType.Pull,
183 MeasuresOfInterest.AverageChangeOverTime,
184 stat => { stat.Value = m_queue.Count(); },
185 StatVerbosity.Debug);
186
187 StatsManager.RegisterStat(s_processedRequestsStat);
188 StatsManager.RegisterStat(s_queuedRequestsStat);
189
190 m_InventoryService = Scene.InventoryService;
191 m_LibraryService = Scene.LibraryService;
192
193 // We'll reuse the same handler for all requests.
194 m_webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, Scene);
195
196 Scene.EventManager.OnRegisterCaps += RegisterCaps;
197
198 m_NumberScenes++;
199
200 int nworkers = 2; // was 2
201 if (ProcessQueuedRequestsAsync && m_workerThreads == null)
202 {
203 m_workerThreads = new Thread[nworkers];
204
205 for (uint i = 0; i < nworkers; i++)
206 {
207 m_workerThreads[i] = WorkManager.StartThread(DoInventoryRequests,
208 String.Format("InventoryWorkerThread{0}", i),
209 ThreadPriority.Normal,
210 true,
211 true,
212 null,
213 int.MaxValue);
214 }
215 }
216 }
217
218 public void PostInitialise()
219 {
220 }
221
222 public void Close()
223 {
224 if (!m_Enabled)
225 return;
226
227 if (ProcessQueuedRequestsAsync)
228 {
229 if (m_NumberScenes <= 0 && m_workerThreads != null)
230 {
231 m_log.DebugFormat("[WebFetchInvDescModule] Closing");
232 foreach (Thread t in m_workerThreads)
233 Watchdog.AbortThread(t.ManagedThreadId);
234
235 m_workerThreads = null;
236 }
237 }
238 }
239
240 public string Name { get { return "WebFetchInvDescModule"; } }
241
242 public Type ReplaceableInterface
243 {
244 get { return null; }
245 }
246
247 #endregion
248
249 private class PollServiceInventoryEventArgs : PollServiceEventArgs
250 {
251 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
252
253 private Dictionary<UUID, Hashtable> responses =
254 new Dictionary<UUID, Hashtable>();
255
256 private WebFetchInvDescModule m_module;
257
258 public PollServiceInventoryEventArgs(WebFetchInvDescModule module, string url, UUID pId) :
259 base(null, url, null, null, null, pId, int.MaxValue)
260 {
261 m_module = module;
262
263 HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); };
264 GetEvents = (x, y) =>
265 {
266 lock (responses)
267 {
268 try
269 {
270 return responses[x];
271 }
272 finally
273 {
274 responses.Remove(x);
275 }
276 }
277 };
278
279 Request = (x, y) =>
280 {
281 ScenePresence sp = m_module.Scene.GetScenePresence(Id);
282
283 aPollRequest reqinfo = new aPollRequest();
284 reqinfo.thepoll = this;
285 reqinfo.reqID = x;
286 reqinfo.request = y;
287 reqinfo.presence = sp;
288 reqinfo.folders = new List<UUID>();
289
290 // Decode the request here
291 string request = y["body"].ToString();
292
293 request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
294
295 request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
296 request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
297
298 Hashtable hash = new Hashtable();
299 try
300 {
301 hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
302 }
303 catch (LLSD.LLSDParseException e)
304 {
305 m_log.ErrorFormat("[INVENTORY]: Fetch error: {0}{1}" + e.Message, e.StackTrace);
306 m_log.Error("Request: " + request);
307 return;
308 }
309 catch (System.Xml.XmlException)
310 {
311 m_log.ErrorFormat("[INVENTORY]: XML Format error");
312 }
313
314 ArrayList foldersrequested = (ArrayList)hash["folders"];
315
316 bool highPriority = false;
317
318 for (int i = 0; i < foldersrequested.Count; i++)
319 {
320 Hashtable inventoryhash = (Hashtable)foldersrequested[i];
321 string folder = inventoryhash["folder_id"].ToString();
322 UUID folderID;
323 if (UUID.TryParse(folder, out folderID))
324 {
325 if (!reqinfo.folders.Contains(folderID))
326 {
327 if (sp.COF != UUID.Zero && sp.COF == folderID)
328 highPriority = true;
329 reqinfo.folders.Add(folderID);
330 }
331 }
332 }
333
334 if (highPriority)
335 m_queue.PriorityEnqueue(reqinfo);
336 else
337 m_queue.Enqueue(reqinfo);
338 };
339
340 NoEvents = (x, y) =>
341 {
342/*
343 lock (requests)
344 {
345 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
346 requests.Remove(request);
347 }
348*/
349 Hashtable response = new Hashtable();
350
351 response["int_response_code"] = 500;
352 response["str_response_string"] = "Script timeout";
353 response["content_type"] = "text/plain";
354 response["keepalive"] = false;
355 response["reusecontext"] = false;
356
357 return response;
358 };
359 }
360
361 public void Process(aPollRequest requestinfo)
362 {
363 if(m_module == null || m_module.Scene == null || m_module.Scene.ShuttingDown)
364 return;
365
366 UUID requestID = requestinfo.reqID;
367
368 Hashtable response = new Hashtable();
369
370 response["int_response_code"] = 200;
371 response["content_type"] = "text/plain";
372 response["keepalive"] = false;
373 response["reusecontext"] = false;
374
375 response["str_response_string"] = m_webFetchHandler.FetchInventoryDescendentsRequest(
376 requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null);
377
378 lock (responses)
379 {
380 if (responses.ContainsKey(requestID))
381 m_log.WarnFormat("[FETCH INVENTORY DESCENDENTS2 MODULE]: Caught in the act of loosing responses! Please report this on mantis #7054");
382 responses[requestID] = response;
383 }
384 requestinfo.folders.Clear();
385 requestinfo.request.Clear();
386 WebFetchInvDescModule.ProcessedRequestsCount++;
387 }
388 }
389
390 private void RegisterCaps(UUID agentID, Caps caps)
391 {
392 RegisterFetchDescendentsCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url);
393 }
394
395 private void RegisterFetchDescendentsCap(UUID agentID, Caps caps, string capName, string url)
396 {
397 string capUrl;
398
399 // disable the cap clause
400 if (url == "")
401 {
402 return;
403 }
404 // handled by the simulator
405 else if (url == "localhost")
406 {
407 capUrl = "/CAPS/" + UUID.Random() + "/";
408
409 // Register this as a poll service
410 PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(this, capUrl, agentID);
411 args.Type = PollServiceEventArgs.EventType.Inventory;
412
413 caps.RegisterPollHandler(capName, args);
414 }
415 // external handler
416 else
417 {
418 capUrl = url;
419 IExternalCapsModule handler = Scene.RequestModuleInterface<IExternalCapsModule>();
420 if (handler != null)
421 handler.RegisterExternalUserCapsHandler(agentID,caps,capName,capUrl);
422 else
423 caps.RegisterHandler(capName, capUrl);
424 }
425
426 // m_log.DebugFormat(
427 // "[FETCH INVENTORY DESCENDENTS2 MODULE]: Registered capability {0} at {1} in region {2} for {3}",
428 // capName, capUrl, m_scene.RegionInfo.RegionName, agentID);
429 }
430
431// private void DeregisterCaps(UUID agentID, Caps caps)
432// {
433// string capUrl;
434//
435// if (m_capsDict.TryGetValue(agentID, out capUrl))
436// {
437// MainServer.Instance.RemoveHTTPHandler("", capUrl);
438// m_capsDict.Remove(agentID);
439// }
440// }
441
442 private static void DoInventoryRequests()
443 {
444 while (true)
445 {
446 aPollRequest poolreq = m_queue.Dequeue(4500);
447 Watchdog.UpdateThread();
448
449 if (poolreq != null && poolreq.thepoll != null)
450 {
451 try
452 {
453 poolreq.thepoll.Process(poolreq);
454 }
455 catch (Exception e)
456 {
457 m_log.ErrorFormat(
458 "[INVENTORY]: Failed to process queued inventory request {0} for {1}. Exception {2}",
459 poolreq.reqID, poolreq.presence != null ? poolreq.presence.Name : "unknown", e);
460 }
461 }
462 }
463 }
464 }
465}