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  
29 using System;
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Net;
33 using System.Reflection;
34 using System.Xml;
35 using log4net;
36 using Nini.Config;
37 using OpenMetaverse;
38 using Mono.Addins;
39 using OpenSim.Framework;
40 using OpenSim.Framework.Communications;
41 using OpenSim.Region.DataSnapshot.Interfaces;
42 using OpenSim.Region.Framework.Interfaces;
43 using OpenSim.Region.Framework.Scenes;
44  
45 [assembly: Addin("DataSnapshot", "0.1")]
46 [assembly: AddinDependency("OpenSim", "0.5")]
47  
48 namespace OpenSim.Region.DataSnapshot
49 {
50 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DataSnapshotManager")]
51 public class DataSnapshotManager : ISharedRegionModule, IDataSnapshot
52 {
53 #region Class members
54 //Information from config
55 private bool m_enabled = false;
56 private bool m_configLoaded = false;
57 private List<string> m_disabledModules = new List<string>();
58 private Dictionary<string, string> m_gridinfo = new Dictionary<string, string>();
59 private string m_snapsDir = "DataSnapshot";
60 private string m_exposure_level = "minimum";
61  
62 //Lists of stuff we need
63 private List<Scene> m_scenes = new List<Scene>();
64 private List<IDataSnapshotProvider> m_dataproviders = new List<IDataSnapshotProvider>();
65  
66 //Various internal objects
67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
68 internal object m_syncInit = new object();
69  
70 //DataServices and networking
71 private string m_dataServices = "noservices";
72 public string m_listener_port = ConfigSettings.DefaultRegionHttpPort.ToString();
73 public string m_hostname = "127.0.0.1";
74 private UUID m_Secret = UUID.Random();
75 private bool m_servicesNotified = false;
76  
77 //Update timers
78 private int m_period = 20; // in seconds
79 private int m_maxStales = 500;
80 private int m_stales = 0;
81 private int m_lastUpdate = 0;
82  
83 //Program objects
84 private SnapshotStore m_snapStore = null;
85  
86 #endregion
87  
88 #region Properties
89  
90 public string ExposureLevel
91 {
92 get { return m_exposure_level; }
93 }
94  
95 public UUID Secret
96 {
97 get { return m_Secret; }
98 }
99  
100 #endregion
101  
102 #region Region Module interface
103  
104 public void Initialise(IConfigSource config)
105 {
106 if (!m_configLoaded)
107 {
108 m_configLoaded = true;
109 //m_log.Debug("[DATASNAPSHOT]: Loading configuration");
110 //Read from the config for options
111 lock (m_syncInit)
112 {
113 try
114 {
115 m_enabled = config.Configs["DataSnapshot"].GetBoolean("index_sims", m_enabled);
116 string gatekeeper = Util.GetConfigVarFromSections<string>(config, "GatekeeperURI",
117 new string[] { "Startup", "Hypergrid", "GridService" }, String.Empty);
118 // Legacy. Remove soon!
119 if (string.IsNullOrEmpty(gatekeeper))
120 {
121 IConfig conf = config.Configs["GridService"];
122 if (conf != null)
123 gatekeeper = conf.GetString("Gatekeeper", gatekeeper);
124 }
125 if (!string.IsNullOrEmpty(gatekeeper))
126 m_gridinfo.Add("gatekeeperURL", gatekeeper);
127  
128 m_gridinfo.Add(
129 "name", config.Configs["DataSnapshot"].GetString("gridname", "the lost continent of hippo"));
130 m_exposure_level = config.Configs["DataSnapshot"].GetString("data_exposure", m_exposure_level);
131 m_period = config.Configs["DataSnapshot"].GetInt("default_snapshot_period", m_period);
132 m_maxStales = config.Configs["DataSnapshot"].GetInt("max_changes_before_update", m_maxStales);
133 m_snapsDir = config.Configs["DataSnapshot"].GetString("snapshot_cache_directory", m_snapsDir);
134 m_dataServices = config.Configs["DataSnapshot"].GetString("data_services", m_dataServices);
135 m_listener_port = config.Configs["Network"].GetString("http_listener_port", m_listener_port);
136  
137 String[] annoying_string_array = config.Configs["DataSnapshot"].GetString("disable_modules", "").Split(".".ToCharArray());
138 foreach (String bloody_wanker in annoying_string_array)
139 {
140 m_disabledModules.Add(bloody_wanker);
141 }
142 m_lastUpdate = Environment.TickCount;
143 }
144 catch (Exception)
145 {
146 m_log.Warn("[DATASNAPSHOT]: Could not load configuration. DataSnapshot will be disabled.");
147 m_enabled = false;
148 return;
149 }
150  
151 }
152  
153 }
154  
155 }
156  
157 public void AddRegion(Scene scene)
158 {
159 if (!m_enabled)
160 return;
161  
162 m_log.DebugFormat("[DATASNAPSHOT]: Module added to Scene {0}.", scene.RegionInfo.RegionName);
163  
164 if (!m_servicesNotified)
165 {
166 m_hostname = scene.RegionInfo.ExternalHostName;
167 m_snapStore = new SnapshotStore(m_snapsDir, m_gridinfo, m_listener_port, m_hostname);
168  
169 //Hand it the first scene, assuming that all scenes have the same BaseHTTPServer
170 new DataRequestHandler(scene, this);
171  
172 if (m_dataServices != "" && m_dataServices != "noservices")
173 NotifyDataServices(m_dataServices, "online");
174  
175 m_servicesNotified = true;
176 }
177  
178 m_scenes.Add(scene);
179 m_snapStore.AddScene(scene);
180  
181 Assembly currentasm = Assembly.GetExecutingAssembly();
182  
183 foreach (Type pluginType in currentasm.GetTypes())
184 {
185 if (pluginType.IsPublic)
186 {
187 if (!pluginType.IsAbstract)
188 {
189 if (pluginType.GetInterface("IDataSnapshotProvider") != null)
190 {
191 IDataSnapshotProvider module = (IDataSnapshotProvider)Activator.CreateInstance(pluginType);
192 module.Initialize(scene, this);
193 module.OnStale += MarkDataStale;
194  
195 m_dataproviders.Add(module);
196 m_snapStore.AddProvider(module);
197  
198 m_log.Debug("[DATASNAPSHOT]: Added new data provider type: " + pluginType.Name);
199 }
200 }
201 }
202 }
203  
204 }
205  
206 public void RemoveRegion(Scene scene)
207 {
208 if (!m_enabled)
209 return;
210  
211 m_log.Info("[DATASNAPSHOT]: Region " + scene.RegionInfo.RegionName + " is being removed, removing from indexing");
212 Scene restartedScene = SceneForUUID(scene.RegionInfo.RegionID);
213  
214 m_scenes.Remove(restartedScene);
215 m_snapStore.RemoveScene(restartedScene);
216  
217 //Getting around the fact that we can't remove objects from a collection we are enumerating over
218 List<IDataSnapshotProvider> providersToRemove = new List<IDataSnapshotProvider>();
219  
220 foreach (IDataSnapshotProvider provider in m_dataproviders)
221 {
222 if (provider.GetParentScene == restartedScene)
223 {
224 providersToRemove.Add(provider);
225 }
226 }
227  
228 foreach (IDataSnapshotProvider provider in providersToRemove)
229 {
230 m_dataproviders.Remove(provider);
231 m_snapStore.RemoveProvider(provider);
232 }
233  
234 m_snapStore.RemoveScene(restartedScene);
235 }
236  
237 public void PostInitialise()
238 {
239 }
240  
241 public void RegionLoaded(Scene scene)
242 {
243 if (!m_enabled)
244 return;
245  
246 m_log.DebugFormat("[DATASNAPSHOT]: Marking scene {0} as stale.", scene.RegionInfo.RegionName);
247 m_snapStore.ForceSceneStale(scene);
248 }
249  
250 public void Close()
251 {
252 if (!m_enabled)
253 return;
254  
255 if (m_enabled && m_dataServices != "" && m_dataServices != "noservices")
256 NotifyDataServices(m_dataServices, "offline");
257 }
258  
259  
260 public string Name
261 {
262 get { return "External Data Generator"; }
263 }
264  
265 public Type ReplaceableInterface
266 {
267 get { return null; }
268 }
269  
270 #endregion
271  
272 #region Associated helper functions
273  
274 public Scene SceneForName(string name)
275 {
276 foreach (Scene scene in m_scenes)
277 if (scene.RegionInfo.RegionName == name)
278 return scene;
279  
280 return null;
281 }
282  
283 public Scene SceneForUUID(UUID id)
284 {
285 foreach (Scene scene in m_scenes)
286 if (scene.RegionInfo.RegionID == id)
287 return scene;
288  
289 return null;
290 }
291  
292 #endregion
293  
294 #region [Public] Snapshot storage functions
295  
296 /**
297 * Reply to the http request
298 */
299 public XmlDocument GetSnapshot(string regionName)
300 {
301 CheckStale();
302  
303 XmlDocument requestedSnap = new XmlDocument();
304 requestedSnap.AppendChild(requestedSnap.CreateXmlDeclaration("1.0", null, null));
305 requestedSnap.AppendChild(requestedSnap.CreateWhitespace("\r\n"));
306  
307 XmlNode regiondata = requestedSnap.CreateNode(XmlNodeType.Element, "regiondata", "");
308 try
309 {
310 if (string.IsNullOrEmpty(regionName))
311 {
312 XmlNode timerblock = requestedSnap.CreateNode(XmlNodeType.Element, "expire", "");
313 timerblock.InnerText = m_period.ToString();
314 regiondata.AppendChild(timerblock);
315  
316 regiondata.AppendChild(requestedSnap.CreateWhitespace("\r\n"));
317 foreach (Scene scene in m_scenes)
318 {
319 regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap));
320 }
321 }
322 else
323 {
324 Scene scene = SceneForName(regionName);
325 regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap));
326 }
327 requestedSnap.AppendChild(regiondata);
328 regiondata.AppendChild(requestedSnap.CreateWhitespace("\r\n"));
329 }
330 catch (XmlException e)
331 {
332 m_log.Warn("[DATASNAPSHOT]: XmlException while trying to load snapshot: " + e.ToString());
333 requestedSnap = GetErrorMessage(regionName, e);
334 }
335 catch (Exception e)
336 {
337 m_log.Warn("[DATASNAPSHOT]: Caught unknown exception while trying to load snapshot: " + e.StackTrace);
338 requestedSnap = GetErrorMessage(regionName, e);
339 }
340  
341  
342 return requestedSnap;
343 }
344  
345 private XmlDocument GetErrorMessage(string regionName, Exception e)
346 {
347 XmlDocument errorMessage = new XmlDocument();
348 XmlNode error = errorMessage.CreateNode(XmlNodeType.Element, "error", "");
349 XmlNode region = errorMessage.CreateNode(XmlNodeType.Element, "region", "");
350 region.InnerText = regionName;
351  
352 XmlNode exception = errorMessage.CreateNode(XmlNodeType.Element, "exception", "");
353 exception.InnerText = e.ToString();
354  
355 error.AppendChild(region);
356 error.AppendChild(exception);
357 errorMessage.AppendChild(error);
358  
359 return errorMessage;
360 }
361  
362 #endregion
363  
364 #region External data services
365 private void NotifyDataServices(string servicesStr, string serviceName)
366 {
367 Stream reply = null;
368 string delimStr = ";";
369 char [] delimiter = delimStr.ToCharArray();
370  
371 string[] services = servicesStr.Split(delimiter);
372  
373 for (int i = 0; i < services.Length; i++)
374 {
375 string url = services[i].Trim();
376 using (RestClient cli = new RestClient(url))
377 {
378 cli.AddQueryParameter("service", serviceName);
379 cli.AddQueryParameter("host", m_hostname);
380 cli.AddQueryParameter("port", m_listener_port);
381 cli.AddQueryParameter("secret", m_Secret.ToString());
382 cli.RequestMethod = "GET";
383 try
384 {
385 reply = cli.Request(null);
386 }
387 catch (WebException)
388 {
389 m_log.Warn("[DATASNAPSHOT]: Unable to notify " + url);
390 }
391 catch (Exception e)
392 {
393 m_log.Warn("[DATASNAPSHOT]: Ignoring unknown exception " + e.ToString());
394 }
395  
396 byte[] response = new byte[1024];
397 // int n = 0;
398 try
399 {
400 // n = reply.Read(response, 0, 1024);
401 reply.Read(response, 0, 1024);
402 }
403 catch (Exception e)
404 {
405 m_log.WarnFormat("[DATASNAPSHOT]: Unable to decode reply from data service. Ignoring. {0}", e.StackTrace);
406 }
407 // This is not quite working, so...
408 // string responseStr = Util.UTF8.GetString(response);
409 m_log.Info("[DATASNAPSHOT]: data service " + url + " notified. Secret: " + m_Secret);
410 }
411 }
412  
413 }
414 #endregion
415  
416 #region Latency-based update functions
417  
418 public void MarkDataStale(IDataSnapshotProvider provider)
419 {
420 //Behavior here: Wait m_period seconds, then update if there has not been a request in m_period seconds
421 //or m_maxStales has been exceeded
422 m_stales++;
423 }
424  
425 private void CheckStale()
426 {
427 // Wrap check
428 if (Environment.TickCount < m_lastUpdate)
429 {
430 m_lastUpdate = Environment.TickCount;
431 }
432  
433 if (m_stales >= m_maxStales)
434 {
435 if (Environment.TickCount - m_lastUpdate >= 20000)
436 {
437 m_stales = 0;
438 m_lastUpdate = Environment.TickCount;
439 MakeEverythingStale();
440 }
441 }
442 else
443 {
444 if (m_lastUpdate + 1000 * m_period < Environment.TickCount)
445 {
446 m_stales = 0;
447 m_lastUpdate = Environment.TickCount;
448 MakeEverythingStale();
449 }
450 }
451 }
452  
453 public void MakeEverythingStale()
454 {
455 m_log.Debug("[DATASNAPSHOT]: Marking all scenes as stale.");
456 foreach (Scene scene in m_scenes)
457 {
458 m_snapStore.ForceSceneStale(scene);
459 }
460 }
461 #endregion
462  
463 }
464 }