clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 OpenMetaverse.StructuredData;
38 using OpenSim.Framework;
39 using OpenSim.Framework.Monitoring;
40 using OpenSim.Framework.Servers;
41 using OpenSim.Framework.Servers.HttpServer;
42 using OpenSim.Region.Framework.Interfaces;
43 using OpenSim.Region.Framework.Scenes;
44 using OpenSim.Framework.Capabilities;
45 using OpenSim.Services.Interfaces;
46 using Caps = OpenSim.Framework.Capabilities.Caps;
47 using OpenSim.Capabilities.Handlers;
48  
49 namespace 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 WebFetchInvDescHandler 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 WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
201  
202 Scene.EventManager.OnRegisterCaps += RegisterCaps;
203  
204 if (ProcessQueuedRequestsAsync && m_workerThreads == null)
205 {
206 m_workerThreads = new Thread[2];
207  
208 for (uint i = 0; i < 2; i++)
209 {
210 m_workerThreads[i] = Watchdog.StartThread(DoInventoryRequests,
211 String.Format("InventoryWorkerThread{0}", i),
212 ThreadPriority.Normal,
213 false,
214 true,
215 null,
216 int.MaxValue);
217 }
218 }
219 }
220  
221 public void PostInitialise()
222 {
223 }
224  
225 public void Close() { }
226  
227 public string Name { get { return "WebFetchInvDescModule"; } }
228  
229 public Type ReplaceableInterface
230 {
231 get { return null; }
232 }
233  
234 #endregion
235  
236 private class PollServiceInventoryEventArgs : PollServiceEventArgs
237 {
238 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
239  
240 private Dictionary<UUID, Hashtable> responses =
241 new Dictionary<UUID, Hashtable>();
242  
243 private WebFetchInvDescModule m_module;
244  
245 public PollServiceInventoryEventArgs(WebFetchInvDescModule module, string url, UUID pId) :
246 base(null, url, null, null, null, pId, int.MaxValue)
247 {
248 m_module = module;
249  
250 HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); };
251 GetEvents = (x, y) =>
252 {
253 lock (responses)
254 {
255 try
256 {
257 return responses[x];
258 }
259 finally
260 {
261 responses.Remove(x);
262 }
263 }
264 };
265  
266 Request = (x, y) =>
267 {
268 ScenePresence sp = m_module.Scene.GetScenePresence(Id);
269 if (sp == null)
270 {
271 m_log.ErrorFormat("[INVENTORY]: Unable to find ScenePresence for {0}", Id);
272 return;
273 }
274  
275 aPollRequest reqinfo = new aPollRequest();
276 reqinfo.thepoll = this;
277 reqinfo.reqID = x;
278 reqinfo.request = y;
279 reqinfo.presence = sp;
280 reqinfo.folders = new List<UUID>();
281  
282 // Decode the request here
283 string request = y["body"].ToString();
284  
285 request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
286  
287 request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
288 request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
289  
290 Hashtable hash = new Hashtable();
291 try
292 {
293 hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
294 }
295 catch (LLSD.LLSDParseException e)
296 {
297 m_log.ErrorFormat("[INVENTORY]: Fetch error: {0}{1}" + e.Message, e.StackTrace);
298 m_log.Error("Request: " + request);
299 return;
300 }
301 catch (System.Xml.XmlException)
302 {
303 m_log.ErrorFormat("[INVENTORY]: XML Format error");
304 }
305  
306 ArrayList foldersrequested = (ArrayList)hash["folders"];
307  
308 bool highPriority = false;
309  
310 for (int i = 0; i < foldersrequested.Count; i++)
311 {
312 Hashtable inventoryhash = (Hashtable)foldersrequested[i];
313 string folder = inventoryhash["folder_id"].ToString();
314 UUID folderID;
315 if (UUID.TryParse(folder, out folderID))
316 {
317 if (!reqinfo.folders.Contains(folderID))
318 {
319 //TODO: Port COF handling from Avination
320 reqinfo.folders.Add(folderID);
321 }
322 }
323 }
324  
325 if (highPriority)
326 m_queue.EnqueueHigh(reqinfo);
327 else
328 m_queue.EnqueueLow(reqinfo);
329 };
330  
331 NoEvents = (x, y) =>
332 {
333 /*
334 lock (requests)
335 {
336 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
337 requests.Remove(request);
338 }
339 */
340 Hashtable response = new Hashtable();
341  
342 response["int_response_code"] = 500;
343 response["str_response_string"] = "Script timeout";
344 response["content_type"] = "text/plain";
345 response["keepalive"] = false;
346 response["reusecontext"] = false;
347  
348 return response;
349 };
350 }
351  
352 public void Process(aPollRequest requestinfo)
353 {
354 UUID requestID = requestinfo.reqID;
355  
356 Hashtable response = new Hashtable();
357  
358 response["int_response_code"] = 200;
359 response["content_type"] = "text/plain";
360 response["keepalive"] = false;
361 response["reusecontext"] = false;
362  
363 response["str_response_string"] = m_webFetchHandler.FetchInventoryDescendentsRequest(
364 requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null);
365  
366 lock (responses)
367 responses[requestID] = response;
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 }