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