aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs350
1 files changed, 318 insertions, 32 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
index 6890f4a..025ffea 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
@@ -27,15 +27,21 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic;
30using System.Reflection; 31using System.Reflection;
32using System.Threading;
31using log4net; 33using log4net;
32using Nini.Config; 34using Nini.Config;
33using Mono.Addins; 35using Mono.Addins;
34using OpenMetaverse; 36using OpenMetaverse;
37using OpenMetaverse.StructuredData;
35using OpenSim.Framework; 38using OpenSim.Framework;
39using OpenSim.Framework.Monitoring;
40using OpenSim.Framework.Servers;
36using OpenSim.Framework.Servers.HttpServer; 41using OpenSim.Framework.Servers.HttpServer;
37using OpenSim.Region.Framework.Interfaces; 42using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes; 43using OpenSim.Region.Framework.Scenes;
44using OpenSim.Framework.Capabilities;
39using OpenSim.Services.Interfaces; 45using OpenSim.Services.Interfaces;
40using Caps = OpenSim.Framework.Capabilities.Caps; 46using Caps = OpenSim.Framework.Capabilities.Caps;
41using OpenSim.Capabilities.Handlers; 47using OpenSim.Capabilities.Handlers;
@@ -48,9 +54,37 @@ namespace OpenSim.Region.ClientStack.Linden
48 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")] 54 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")]
49 public class WebFetchInvDescModule : INonSharedRegionModule 55 public class WebFetchInvDescModule : INonSharedRegionModule
50 { 56 {
51// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 57 class aPollRequest
58 {
59 public PollServiceInventoryEventArgs thepoll;
60 public UUID reqID;
61 public Hashtable request;
62 public ScenePresence presence;
63 public List<UUID> folders;
64 }
65
66 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
67
68 /// <summary>
69 /// Control whether requests will be processed asynchronously.
70 /// </summary>
71 /// <remarks>
72 /// Defaults to true. Can currently not be changed once a region has been added to the module.
73 /// </remarks>
74 public bool ProcessQueuedRequestsAsync { get; private set; }
52 75
53 private Scene m_scene; 76 /// <summary>
77 /// Number of inventory requests processed by this module.
78 /// </summary>
79 /// <remarks>
80 /// It's the PollServiceRequestManager that actually sends completed requests back to the requester.
81 /// </remarks>
82 public static int ProcessedRequestsCount { get; set; }
83
84 private static Stat s_queuedRequestsStat;
85 private static Stat s_processedRequestsStat;
86
87 public Scene Scene { get; private set; }
54 88
55 private IInventoryService m_InventoryService; 89 private IInventoryService m_InventoryService;
56 private ILibraryService m_LibraryService; 90 private ILibraryService m_LibraryService;
@@ -60,10 +94,22 @@ namespace OpenSim.Region.ClientStack.Linden
60 private string m_fetchInventoryDescendents2Url; 94 private string m_fetchInventoryDescendents2Url;
61 private string m_webFetchInventoryDescendentsUrl; 95 private string m_webFetchInventoryDescendentsUrl;
62 96
63 private WebFetchInvDescHandler m_webFetchHandler; 97 private static FetchInvDescHandler m_webFetchHandler;
98
99 private static Thread[] m_workerThreads = null;
100
101 private static DoubleQueue<aPollRequest> m_queue =
102 new DoubleQueue<aPollRequest>();
64 103
65 #region ISharedRegionModule Members 104 #region ISharedRegionModule Members
66 105
106 public WebFetchInvDescModule() : this(true) {}
107
108 public WebFetchInvDescModule(bool processQueuedResultsAsync)
109 {
110 ProcessQueuedRequestsAsync = processQueuedResultsAsync;
111 }
112
67 public void Initialise(IConfigSource source) 113 public void Initialise(IConfigSource source)
68 { 114 {
69 IConfig config = source.Configs["ClientStack.LindenCaps"]; 115 IConfig config = source.Configs["ClientStack.LindenCaps"];
@@ -84,7 +130,7 @@ namespace OpenSim.Region.ClientStack.Linden
84 if (!m_Enabled) 130 if (!m_Enabled)
85 return; 131 return;
86 132
87 m_scene = s; 133 Scene = s;
88 } 134 }
89 135
90 public void RemoveRegion(Scene s) 136 public void RemoveRegion(Scene s)
@@ -92,8 +138,23 @@ namespace OpenSim.Region.ClientStack.Linden
92 if (!m_Enabled) 138 if (!m_Enabled)
93 return; 139 return;
94 140
95 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 141 Scene.EventManager.OnRegisterCaps -= RegisterCaps;
96 m_scene = null; 142
143 StatsManager.DeregisterStat(s_processedRequestsStat);
144 StatsManager.DeregisterStat(s_queuedRequestsStat);
145
146 if (ProcessQueuedRequestsAsync)
147 {
148 if (m_workerThreads != null)
149 {
150 foreach (Thread t in m_workerThreads)
151 Watchdog.AbortThread(t.ManagedThreadId);
152
153 m_workerThreads = null;
154 }
155 }
156
157 Scene = null;
97 } 158 }
98 159
99 public void RegionLoaded(Scene s) 160 public void RegionLoaded(Scene s)
@@ -101,14 +162,61 @@ namespace OpenSim.Region.ClientStack.Linden
101 if (!m_Enabled) 162 if (!m_Enabled)
102 return; 163 return;
103 164
104 m_InventoryService = m_scene.InventoryService; 165 if (s_processedRequestsStat == null)
105 m_LibraryService = m_scene.LibraryService; 166 s_processedRequestsStat =
167 new Stat(
168 "ProcessedFetchInventoryRequests",
169 "Number of processed fetch inventory requests",
170 "These have not necessarily yet been dispatched back to the requester.",
171 "",
172 "inventory",
173 "httpfetch",
174 StatType.Pull,
175 MeasuresOfInterest.AverageChangeOverTime,
176 stat => { stat.Value = ProcessedRequestsCount; },
177 StatVerbosity.Debug);
178
179 if (s_queuedRequestsStat == null)
180 s_queuedRequestsStat =
181 new Stat(
182 "QueuedFetchInventoryRequests",
183 "Number of fetch inventory requests queued for processing",
184 "",
185 "",
186 "inventory",
187 "httpfetch",
188 StatType.Pull,
189 MeasuresOfInterest.AverageChangeOverTime,
190 stat => { stat.Value = m_queue.Count; },
191 StatVerbosity.Debug);
192
193 StatsManager.RegisterStat(s_processedRequestsStat);
194 StatsManager.RegisterStat(s_queuedRequestsStat);
195
196 m_InventoryService = Scene.InventoryService;
197 m_LibraryService = Scene.LibraryService;
106 198
107 // We'll reuse the same handler for all requests. 199 // We'll reuse the same handler for all requests.
108 if (m_fetchInventoryDescendents2Url == "localhost" || m_webFetchInventoryDescendentsUrl == "localhost") 200 m_webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, Scene);
109 m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
110 201
111 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 202 Scene.EventManager.OnRegisterCaps += RegisterCaps;
203
204 int nworkers = 2; // was 2
205 if (ProcessQueuedRequestsAsync && m_workerThreads == null)
206 {
207 m_workerThreads = new Thread[nworkers];
208
209 for (uint i = 0; i < nworkers; i++)
210 {
211 m_workerThreads[i] = WorkManager.StartThread(DoInventoryRequests,
212 String.Format("InventoryWorkerThread{0}", i),
213 ThreadPriority.Normal,
214 false,
215 true,
216 null,
217 int.MaxValue);
218 }
219 }
112 } 220 }
113 221
114 public void PostInitialise() 222 public void PostInitialise()
@@ -126,43 +234,221 @@ namespace OpenSim.Region.ClientStack.Linden
126 234
127 #endregion 235 #endregion
128 236
129 private void RegisterCaps(UUID agentID, Caps caps) 237 private class PollServiceInventoryEventArgs : PollServiceEventArgs
130 { 238 {
131 if (m_webFetchInventoryDescendentsUrl != "") 239 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
132 RegisterFetchCap(agentID, caps, "WebFetchInventoryDescendents", m_webFetchInventoryDescendentsUrl); 240
241 private Dictionary<UUID, Hashtable> responses =
242 new Dictionary<UUID, Hashtable>();
243
244 private WebFetchInvDescModule m_module;
133 245
134 if (m_fetchInventoryDescendents2Url != "") 246 public PollServiceInventoryEventArgs(WebFetchInvDescModule module, string url, UUID pId) :
135 RegisterFetchCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url); 247 base(null, url, null, null, null, pId, int.MaxValue)
248 {
249 m_module = module;
250
251 HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); };
252 GetEvents = (x, y) =>
253 {
254 lock (responses)
255 {
256 try
257 {
258 return responses[x];
259 }
260 finally
261 {
262 responses.Remove(x);
263 }
264 }
265 };
266
267 Request = (x, y) =>
268 {
269 ScenePresence sp = m_module.Scene.GetScenePresence(Id);
270
271 aPollRequest reqinfo = new aPollRequest();
272 reqinfo.thepoll = this;
273 reqinfo.reqID = x;
274 reqinfo.request = y;
275 reqinfo.presence = sp;
276 reqinfo.folders = new List<UUID>();
277
278 // Decode the request here
279 string request = y["body"].ToString();
280
281 request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
282
283 request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
284 request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
285
286 Hashtable hash = new Hashtable();
287 try
288 {
289 hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
290 }
291 catch (LLSD.LLSDParseException e)
292 {
293 m_log.ErrorFormat("[INVENTORY]: Fetch error: {0}{1}" + e.Message, e.StackTrace);
294 m_log.Error("Request: " + request);
295 return;
296 }
297 catch (System.Xml.XmlException)
298 {
299 m_log.ErrorFormat("[INVENTORY]: XML Format error");
300 }
301
302 ArrayList foldersrequested = (ArrayList)hash["folders"];
303
304 bool highPriority = false;
305
306 for (int i = 0; i < foldersrequested.Count; i++)
307 {
308 Hashtable inventoryhash = (Hashtable)foldersrequested[i];
309 string folder = inventoryhash["folder_id"].ToString();
310 UUID folderID;
311 if (UUID.TryParse(folder, out folderID))
312 {
313 if (!reqinfo.folders.Contains(folderID))
314 {
315 //TODO: Port COF handling from Avination
316 reqinfo.folders.Add(folderID);
317 }
318 }
319 }
320
321 if (highPriority)
322 m_queue.EnqueueHigh(reqinfo);
323 else
324 m_queue.EnqueueLow(reqinfo);
325 };
326
327 NoEvents = (x, y) =>
328 {
329/*
330 lock (requests)
331 {
332 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
333 requests.Remove(request);
334 }
335*/
336 Hashtable response = new Hashtable();
337
338 response["int_response_code"] = 500;
339 response["str_response_string"] = "Script timeout";
340 response["content_type"] = "text/plain";
341 response["keepalive"] = false;
342 response["reusecontext"] = false;
343
344 return response;
345 };
346 }
347
348 public void Process(aPollRequest requestinfo)
349 {
350 UUID requestID = requestinfo.reqID;
351
352 Hashtable response = new Hashtable();
353
354 response["int_response_code"] = 200;
355 response["content_type"] = "text/plain";
356 response["keepalive"] = false;
357 response["reusecontext"] = false;
358
359 response["str_response_string"] = m_webFetchHandler.FetchInventoryDescendentsRequest(
360 requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null);
361
362 lock (responses)
363 {
364 if (responses.ContainsKey(requestID))
365 m_log.WarnFormat("[FETCH INVENTORY DESCENDENTS2 MODULE]: Caught in the act of loosing responses! Please report this on mantis #7054");
366 responses[requestID] = response;
367 }
368
369 WebFetchInvDescModule.ProcessedRequestsCount++;
370 }
371 }
372
373 private void RegisterCaps(UUID agentID, Caps caps)
374 {
375 RegisterFetchDescendentsCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url);
136 } 376 }
137 377
138 private void RegisterFetchCap(UUID agentID, Caps caps, string capName, string url) 378 private void RegisterFetchDescendentsCap(UUID agentID, Caps caps, string capName, string url)
139 { 379 {
140 string capUrl; 380 string capUrl;
141 381
142 if (url == "localhost") 382 // disable the cap clause
383 if (url == "")
384 {
385 return;
386 }
387 // handled by the simulator
388 else if (url == "localhost")
143 { 389 {
144 capUrl = "/CAPS/" + UUID.Random(); 390 capUrl = "/CAPS/" + UUID.Random() + "/";
145 391
146 IRequestHandler reqHandler 392 // Register this as a poll service
147 = new RestStreamHandler( 393 PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(this, capUrl, agentID);
148 "POST", 394 args.Type = PollServiceEventArgs.EventType.Inventory;
149 capUrl,
150 m_webFetchHandler.FetchInventoryDescendentsRequest,
151 "FetchInventoryDescendents2",
152 agentID.ToString());
153 395
154 caps.RegisterHandler(capName, reqHandler); 396 caps.RegisterPollHandler(capName, args);
155 } 397 }
398 // external handler
156 else 399 else
157 { 400 {
158 capUrl = url; 401 capUrl = url;
402 IExternalCapsModule handler = Scene.RequestModuleInterface<IExternalCapsModule>();
403 if (handler != null)
404 handler.RegisterExternalUserCapsHandler(agentID,caps,capName,capUrl);
405 else
406 caps.RegisterHandler(capName, capUrl);
407 }
159 408
160 caps.RegisterHandler(capName, capUrl); 409 // m_log.DebugFormat(
410 // "[FETCH INVENTORY DESCENDENTS2 MODULE]: Registered capability {0} at {1} in region {2} for {3}",
411 // capName, capUrl, m_scene.RegionInfo.RegionName, agentID);
412 }
413
414// private void DeregisterCaps(UUID agentID, Caps caps)
415// {
416// string capUrl;
417//
418// if (m_capsDict.TryGetValue(agentID, out capUrl))
419// {
420// MainServer.Instance.RemoveHTTPHandler("", capUrl);
421// m_capsDict.Remove(agentID);
422// }
423// }
424
425 private void DoInventoryRequests()
426 {
427 while (true)
428 {
429 Watchdog.UpdateThread();
430
431 WaitProcessQueuedInventoryRequest();
161 } 432 }
433 }
434
435 public void WaitProcessQueuedInventoryRequest()
436 {
437 aPollRequest poolreq = m_queue.Dequeue();
162 438
163// m_log.DebugFormat( 439 if (poolreq != null && poolreq.thepoll != null)
164// "[WEB FETCH INV DESC MODULE]: Registered capability {0} at {1} in region {2} for {3}", 440 {
165// capName, capUrl, m_scene.RegionInfo.RegionName, agentID); 441 try
442 {
443 poolreq.thepoll.Process(poolreq);
444 }
445 catch (Exception e)
446 {
447 m_log.ErrorFormat(
448 "[INVENTORY]: Failed to process queued inventory request {0} for {1} in {2}. Exception {3}",
449 poolreq.reqID, poolreq.presence != null ? poolreq.presence.Name : "unknown", Scene.Name, e);
450 }
451 }
166 } 452 }
167 } 453 }
168} \ No newline at end of file 454}