OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
WebFetchInvDescModule.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Reflection;
32 using System.Threading;
33 using log4net;
34 using Nini.Config;
35 using Mono.Addins;
36 using OpenMetaverse;
37 using OpenSim.Framework;
38 using OpenSim.Framework.Servers;
39 using OpenSim.Framework.Servers.HttpServer;
40 using OpenSim.Region.Framework.Interfaces;
41 using OpenSim.Region.Framework.Scenes;
42 using OpenSim.Framework.Capabilities;
43 using OpenSim.Services.Interfaces;
45 using OpenSim.Capabilities.Handlers;
46 using OpenSim.Framework.Monitoring;
47 
48 using OpenMetaverse.StructuredData;
49 
50 namespace OpenSim.Region.ClientStack.Linden
51 {
55  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")]
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 
75  public bool ProcessQueuedRequestsAsync { get; private set; }
76 
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  false,
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  Watchdog.UpdateThread();
447 
448  aPollRequest poolreq = m_queue.Dequeue(5000);
449 
450  if (poolreq != null && poolreq.thepoll != null)
451  {
452  try
453  {
454  poolreq.thepoll.Process(poolreq);
455  }
456  catch (Exception e)
457  {
458  m_log.ErrorFormat(
459  "[INVENTORY]: Failed to process queued inventory request {0} for {1}. Exception {3}",
460  poolreq.reqID, poolreq.presence != null ? poolreq.presence.Name : "unknown", e);
461  }
462  }
463  }
464  }
465  }
466 }
OpenSim.Server.Handlers.Simulation.Utils Utils
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
Holds individual statistic details
Definition: Stat.cs:41
This module implements both WebFetchInventoryDescendents and FetchInventoryDescendents2 capabilities...
OpenSim.Framework.Capabilities.Caps Caps
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
void RemoveRegion(Scene s)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
void AddRegion(Scene s)
This is called whenever a Scene is added. For shared modules, this can happen several times...
MeasuresOfInterest
Measures of interest for this stat.
void RegionLoaded(Scene s)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...