30 using System.Collections.Generic;
34 using System.Reflection;
41 using OpenSim.Framework;
42 using OpenSim.Region.DataSnapshot.Interfaces;
43 using OpenSim.Region.Framework.Interfaces;
44 using OpenSim.Region.Framework.Scenes;
46 namespace OpenSim.
Region.DataSnapshot
48 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"DataSnapshotManager")]
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";
61 private List<Scene> m_scenes =
new List<Scene>();
62 private List<IDataSnapshotProvider> m_dataproviders =
new List<IDataSnapshotProvider>();
65 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
66 internal object m_syncInit =
new object();
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;
76 private int m_period = 20;
77 private int m_maxStales = 500;
78 private int m_stales = 0;
79 private int m_lastUpdate = 0;
88 public string ExposureLevel
90 get {
return m_exposure_level; }
95 get {
return m_Secret; }
100 #region Region Module interface
106 m_configLoaded =
true;
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);
117 if (
string.IsNullOrEmpty(gatekeeper))
119 IConfig conf = config.Configs[
"GridService"];
121 gatekeeper = conf.GetString(
"Gatekeeper", gatekeeper);
123 if (!
string.IsNullOrEmpty(gatekeeper))
124 m_gridinfo.Add(
"gatekeeperURL", gatekeeper);
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);
134 m_dataServices = config.Configs[
"DataSnapshot"].GetString(
"data_services", m_dataServices);
136 AddDataServicesVars(config.Configs[
"DataSnapshot"]);
138 String[] annoying_string_array = config.Configs[
"DataSnapshot"].GetString(
"disable_modules",
"").Split(
".".ToCharArray());
139 foreach (
String bloody_wanker
in annoying_string_array)
141 m_disabledModules.Add(bloody_wanker);
143 m_lastUpdate = Environment.TickCount;
147 m_log.Warn(
"[DATASNAPSHOT]: Could not load configuration. DataSnapshot will be disabled.");
163 m_log.DebugFormat(
"[DATASNAPSHOT]: Module added to Scene {0}.", scene.RegionInfo.RegionName);
165 if (!m_servicesNotified)
167 m_hostname = scene.RegionInfo.ExternalHostName;
168 m_snapStore =
new SnapshotStore(m_snapsDir, m_gridinfo, m_listener_port, m_hostname);
173 if (m_dataServices !=
"" && m_dataServices !=
"noservices")
174 NotifyDataServices(m_dataServices,
"online");
176 m_servicesNotified =
true;
180 m_snapStore.AddScene(scene);
182 Assembly currentasm = Assembly.GetExecutingAssembly();
184 foreach (
Type pluginType
in currentasm.GetTypes())
186 if (pluginType.IsPublic)
188 if (!pluginType.IsAbstract)
190 if (pluginType.GetInterface(
"IDataSnapshotProvider") != null)
193 module.Initialize(scene,
this);
194 module.OnStale += MarkDataStale;
196 m_dataproviders.Add(module);
197 m_snapStore.AddProvider(module);
199 m_log.Debug(
"[DATASNAPSHOT]: Added new data provider type: " + pluginType.Name);
212 m_log.Info(
"[DATASNAPSHOT]: Region " + scene.RegionInfo.RegionName +
" is being removed, removing from indexing");
215 m_scenes.Remove(restartedScene);
216 m_snapStore.RemoveScene(restartedScene);
219 List<IDataSnapshotProvider> providersToRemove =
new List<IDataSnapshotProvider>();
225 providersToRemove.Add(provider);
231 m_dataproviders.Remove(provider);
232 m_snapStore.RemoveProvider(provider);
235 m_snapStore.RemoveScene(restartedScene);
247 m_log.DebugFormat(
"[DATASNAPSHOT]: Marking scene {0} as stale.", scene.RegionInfo.RegionName);
248 m_snapStore.ForceSceneStale(scene);
256 if (m_enabled && m_dataServices !=
"" && m_dataServices !=
"noservices")
257 NotifyDataServices(m_dataServices,
"offline");
263 get {
return "External Data Generator"; }
266 public Type ReplaceableInterface
273 #region Associated helper functions
277 foreach (
Scene scene
in m_scenes)
286 foreach (
Scene scene
in m_scenes)
293 private void AddDataServicesVars(IConfig config)
296 List<string> servs =
new List<string>(m_dataServices.Split(
new char[] {
';' }));
298 StringBuilder sb =
new StringBuilder();
299 string[] keys = config.GetKeys();
304 foreach (
string serviceKey
in serviceKeys)
306 string keyValue = config.GetString(serviceKey, string.Empty).Trim();
307 if (!servs.Contains(keyValue))
308 sb.Append(keyValue).Append(
";");
312 m_dataServices = (m_dataServices ==
"noservices") ? sb.ToString() : sb.Append(m_dataServices).ToString();
317 #region [Public] Snapshot storage functions
326 XmlDocument requestedSnap =
new XmlDocument();
327 requestedSnap.AppendChild(requestedSnap.CreateXmlDeclaration(
"1.0", null, null));
328 requestedSnap.AppendChild(requestedSnap.CreateWhitespace(
"\r\n"));
330 XmlNode regiondata = requestedSnap.CreateNode(XmlNodeType.Element,
"regiondata",
"");
333 if (
string.IsNullOrEmpty(regionName))
335 XmlNode timerblock = requestedSnap.CreateNode(XmlNodeType.Element,
"expire",
"");
336 timerblock.InnerText = m_period.ToString();
337 regiondata.AppendChild(timerblock);
339 regiondata.AppendChild(requestedSnap.CreateWhitespace(
"\r\n"));
340 foreach (
Scene scene
in m_scenes)
342 regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap));
347 Scene scene = SceneForName(regionName);
348 regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap));
350 requestedSnap.AppendChild(regiondata);
351 regiondata.AppendChild(requestedSnap.CreateWhitespace(
"\r\n"));
353 catch (XmlException e)
355 m_log.Warn(
"[DATASNAPSHOT]: XmlException while trying to load snapshot: " + e.ToString());
356 requestedSnap = GetErrorMessage(regionName, e);
360 m_log.Warn(
"[DATASNAPSHOT]: Caught unknown exception while trying to load snapshot: " + e.StackTrace);
361 requestedSnap = GetErrorMessage(regionName, e);
365 return requestedSnap;
368 private XmlDocument GetErrorMessage(
string regionName, Exception e)
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;
375 XmlNode exception = errorMessage.CreateNode(XmlNodeType.Element,
"exception",
"");
376 exception.InnerText = e.ToString();
378 error.AppendChild(region);
379 error.AppendChild(exception);
380 errorMessage.AppendChild(error);
387 #region External data services
388 private void NotifyDataServices(
string servicesStr,
string serviceName)
391 string delimStr =
";";
392 char [] delimiter = delimStr.ToCharArray();
394 string[] services = servicesStr.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
396 for (
int i = 0; i < services.Length; i++)
398 string url = services[i].Trim();
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";
408 reply = cli.Request(null);
412 m_log.Warn(
"[DATASNAPSHOT]: Unable to notify " + url);
416 m_log.Warn(
"[DATASNAPSHOT]: Ignoring unknown exception " + e.ToString());
419 byte[] response =
new byte[1024];
424 reply.Read(response, 0, 1024);
428 m_log.WarnFormat(
"[DATASNAPSHOT]: Unable to decode reply from data service. Ignoring. {0}", e.StackTrace);
432 m_log.Info(
"[DATASNAPSHOT]: data service " + url +
" notified. Secret: " + m_Secret);
438 #region Latency-based update functions
447 private void CheckStale()
450 if (Environment.TickCount < m_lastUpdate)
452 m_lastUpdate = Environment.TickCount;
455 if (m_stales >= m_maxStales)
457 if (Environment.TickCount - m_lastUpdate >= 20000)
460 m_lastUpdate = Environment.TickCount;
461 MakeEverythingStale();
466 if (m_lastUpdate + 1000 * m_period < Environment.TickCount)
469 m_lastUpdate = Environment.TickCount;
470 MakeEverythingStale();
477 m_log.Debug(
"[DATASNAPSHOT]: Marking all scenes as stale.");
478 foreach (
Scene scene
in m_scenes)
480 m_snapStore.ForceSceneStale(scene);
void MarkDataStale(IDataSnapshotProvider provider)
XmlDocument GetSnapshot(string regionName)
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
System.Collections.IEnumerable IEnumerable
void MakeEverythingStale()
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
Implementation of a generic REST client
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
virtual RegionInfo RegionInfo
Scene SceneForName(string name)
Scene SceneForUUID(UUID id)