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.cs454
1 files changed, 454 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..025ffea
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
@@ -0,0 +1,454 @@
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 OpenMetaverse.StructuredData;
38using OpenSim.Framework;
39using OpenSim.Framework.Monitoring;
40using OpenSim.Framework.Servers;
41using OpenSim.Framework.Servers.HttpServer;
42using OpenSim.Region.Framework.Interfaces;
43using OpenSim.Region.Framework.Scenes;
44using OpenSim.Framework.Capabilities;
45using OpenSim.Services.Interfaces;
46using Caps = OpenSim.Framework.Capabilities.Caps;
47using OpenSim.Capabilities.Handlers;
48
49namespace OpenSim.Region.ClientStack.Linden
50{
51 /// <summary>
52 /// This module implements both WebFetchInventoryDescendents and FetchInventoryDescendents2 capabilities.
53 /// </summary>
54 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")]
55 public class WebFetchInvDescModule : INonSharedRegionModule
56 {
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; }
75
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; }
88
89 private IInventoryService m_InventoryService;
90 private ILibraryService m_LibraryService;
91
92 private bool m_Enabled;
93
94 private string m_fetchInventoryDescendents2Url;
95 private string m_webFetchInventoryDescendentsUrl;
96
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>();
103
104 #region ISharedRegionModule Members
105
106 public WebFetchInvDescModule() : this(true) {}
107
108 public WebFetchInvDescModule(bool processQueuedResultsAsync)
109 {
110 ProcessQueuedRequestsAsync = processQueuedResultsAsync;
111 }
112
113 public void Initialise(IConfigSource source)
114 {
115 IConfig config = source.Configs["ClientStack.LindenCaps"];
116 if (config == null)
117 return;
118
119 m_fetchInventoryDescendents2Url = config.GetString("Cap_FetchInventoryDescendents2", string.Empty);
120 m_webFetchInventoryDescendentsUrl = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty);
121
122 if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty)
123 {
124 m_Enabled = true;
125 }
126 }
127
128 public void AddRegion(Scene s)
129 {
130 if (!m_Enabled)
131 return;
132
133 Scene = s;
134 }
135
136 public void RemoveRegion(Scene s)
137 {
138 if (!m_Enabled)
139 return;
140
141 Scene.EventManager.OnRegisterCaps -= RegisterCaps;
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;
158 }
159
160 public void RegionLoaded(Scene s)
161 {
162 if (!m_Enabled)
163 return;
164
165 if (s_processedRequestsStat == null)
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;
198
199 // We'll reuse the same handler for all requests.
200 m_webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, Scene);
201
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 }
220 }
221
222 public void PostInitialise()
223 {
224 }
225
226 public void Close() { }
227
228 public string Name { get { return "WebFetchInvDescModule"; } }
229
230 public Type ReplaceableInterface
231 {
232 get { return null; }
233 }
234
235 #endregion
236
237 private class PollServiceInventoryEventArgs : PollServiceEventArgs
238 {
239 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
240
241 private Dictionary<UUID, Hashtable> responses =
242 new Dictionary<UUID, Hashtable>();
243
244 private WebFetchInvDescModule m_module;
245
246 public PollServiceInventoryEventArgs(WebFetchInvDescModule module, string url, UUID pId) :
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);
376 }
377
378 private void RegisterFetchDescendentsCap(UUID agentID, Caps caps, string capName, string url)
379 {
380 string capUrl;
381
382 // disable the cap clause
383 if (url == "")
384 {
385 return;
386 }
387 // handled by the simulator
388 else if (url == "localhost")
389 {
390 capUrl = "/CAPS/" + UUID.Random() + "/";
391
392 // Register this as a poll service
393 PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(this, capUrl, agentID);
394 args.Type = PollServiceEventArgs.EventType.Inventory;
395
396 caps.RegisterPollHandler(capName, args);
397 }
398 // external handler
399 else
400 {
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 }
408
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();
432 }
433 }
434
435 public void WaitProcessQueuedInventoryRequest()
436 {
437 aPollRequest poolreq = m_queue.Dequeue();
438
439 if (poolreq != null && poolreq.thepoll != null)
440 {
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 }
452 }
453 }
454}