SnapshotStore.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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 OpenSim 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. using System;
  28. using System.Collections.Generic;
  29. using System.Text;
  30. using System.Xml;
  31. using System.IO;
  32. using OpenSim.Region.Environment.Scenes;
  33. using OpenSim.Region.DataSnapshot.Interfaces;
  34. using OpenMetaverse;
  35. namespace OpenSim.Region.DataSnapshot
  36. {
  37. public class SnapshotStore
  38. {
  39. #region Class Members
  40. private String m_directory = "unyuu"; //not an attempt at adding RM references to core SVN, honest
  41. private Dictionary<Scene, bool> m_scenes = null;
  42. private List<IDataSnapshotProvider> m_providers = null;
  43. private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  44. private Dictionary<String, String> m_gridinfo = null;
  45. private bool m_cacheEnabled = true;
  46. private string m_listener_port = "9000"; //TODO: Set default port over 9000
  47. private string m_hostname = "127.0.0.1";
  48. #endregion
  49. public SnapshotStore(string directory, Dictionary<String, String> gridinfo, string port, string hostname) {
  50. m_directory = directory;
  51. m_scenes = new Dictionary<Scene, bool>();
  52. m_providers = new List<IDataSnapshotProvider>();
  53. m_gridinfo = gridinfo;
  54. m_listener_port = port;
  55. m_hostname = hostname;
  56. if (Directory.Exists(m_directory))
  57. {
  58. m_log.Info("[DATASNAPSHOT]: Response and fragment cache directory already exists.");
  59. }
  60. else
  61. {
  62. // Try to create the directory.
  63. m_log.Info("[DATASNAPSHOT]: Creating directory " + m_directory);
  64. try
  65. {
  66. Directory.CreateDirectory(m_directory);
  67. }
  68. catch (Exception e)
  69. {
  70. m_log.Error("[DATASNAPSHOT]: Failed to create directory " + m_directory, e);
  71. //This isn't a horrible problem, just disable cacheing.
  72. m_cacheEnabled = false;
  73. m_log.Error("[DATASNAPSHOT]: Could not create directory, response cache has been disabled.");
  74. }
  75. }
  76. }
  77. public void ForceSceneStale(Scene scene) {
  78. m_scenes[scene] = true;
  79. }
  80. #region Fragment storage
  81. public XmlNode GetFragment(IDataSnapshotProvider provider, XmlDocument factory)
  82. {
  83. XmlNode data = null;
  84. if (provider.Stale || !m_cacheEnabled)
  85. {
  86. data = provider.RequestSnapshotData(factory);
  87. if (m_cacheEnabled)
  88. {
  89. String path = DataFileNameFragment(provider.GetParentScene, provider.Name);
  90. using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default))
  91. {
  92. snapXWriter.Formatting = Formatting.Indented;
  93. snapXWriter.WriteStartDocument();
  94. data.WriteTo(snapXWriter);
  95. snapXWriter.WriteEndDocument();
  96. }
  97. }
  98. //mark provider as not stale, parent scene as stale
  99. provider.Stale = false;
  100. m_scenes[provider.GetParentScene] = true;
  101. m_log.Info("[DATASNAPSHOT]: Generated fragment response for provider type " + provider.Name);
  102. }
  103. else
  104. {
  105. String path = DataFileNameFragment(provider.GetParentScene, provider.Name);
  106. XmlDocument fragDocument = new XmlDocument();
  107. fragDocument.PreserveWhitespace = true;
  108. fragDocument.Load(path);
  109. foreach (XmlNode node in fragDocument)
  110. {
  111. data = factory.ImportNode(node, true);
  112. }
  113. m_log.Info("[DATASNAPSHOT]: Retrieved fragment response for provider type " + provider.Name);
  114. }
  115. return data;
  116. }
  117. #endregion
  118. #region Response storage
  119. public XmlNode GetScene(Scene scene, XmlDocument factory)
  120. {
  121. m_log.Debug("[DATASNAPSHOT]: Data requested for scene " + scene.RegionInfo.RegionName);
  122. if (!m_scenes.ContainsKey(scene)) {
  123. m_scenes.Add(scene, true); //stale by default
  124. }
  125. XmlNode regionElement = null;
  126. if (!m_scenes[scene])
  127. {
  128. m_log.Info("[DATASNAPSHOT]: Attempting to retrieve snapshot from cache.");
  129. //get snapshot from cache
  130. String path = DataFileNameScene(scene);
  131. XmlDocument fragDocument = new XmlDocument();
  132. fragDocument.PreserveWhitespace = true;
  133. fragDocument.Load(path);
  134. foreach (XmlNode node in fragDocument)
  135. {
  136. regionElement = factory.ImportNode(node, true);
  137. }
  138. m_log.Info("[DATASNAPSHOT]: Obtained snapshot from cache for " + scene.RegionInfo.RegionName);
  139. }
  140. else
  141. {
  142. m_log.Info("[DATASNAPSHOT]: Attempting to generate snapshot.");
  143. //make snapshot
  144. regionElement = MakeRegionNode(scene, factory);
  145. regionElement.AppendChild(GetGridSnapshotData(factory));
  146. XmlNode regionData = factory.CreateNode(XmlNodeType.Element, "data", "");
  147. foreach (IDataSnapshotProvider dataprovider in m_providers)
  148. {
  149. if (dataprovider.GetParentScene == scene)
  150. {
  151. regionData.AppendChild(GetFragment(dataprovider, factory));
  152. }
  153. }
  154. regionElement.AppendChild(regionData);
  155. factory.AppendChild(regionElement);
  156. //save snapshot
  157. String path = DataFileNameScene(scene);
  158. using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default))
  159. {
  160. snapXWriter.Formatting = Formatting.Indented;
  161. snapXWriter.WriteStartDocument();
  162. regionElement.WriteTo(snapXWriter);
  163. snapXWriter.WriteEndDocument();
  164. }
  165. m_scenes[scene] = false;
  166. m_log.Info("[DATASNAPSHOT]: Generated new snapshot for " + scene.RegionInfo.RegionName);
  167. }
  168. return regionElement;
  169. }
  170. #endregion
  171. #region Helpers
  172. private string DataFileNameFragment(Scene scene, String fragmentName)
  173. {
  174. return Path.Combine(m_directory, Path.ChangeExtension(scene.RegionInfo.RegionName + "_" + fragmentName, "xml"));
  175. }
  176. private string DataFileNameScene(Scene scene)
  177. {
  178. return Path.Combine(m_directory, Path.ChangeExtension(scene.RegionInfo.RegionName, "xml"));
  179. //return (m_snapsDir + Path.DirectorySeparatorChar + scene.RegionInfo.RegionName + ".xml");
  180. }
  181. private XmlNode MakeRegionNode(Scene scene, XmlDocument basedoc)
  182. {
  183. XmlNode docElement = basedoc.CreateNode(XmlNodeType.Element, "region", "");
  184. XmlAttribute attr = basedoc.CreateAttribute("category");
  185. attr.Value = GetRegionCategory(scene);
  186. docElement.Attributes.Append(attr);
  187. attr = basedoc.CreateAttribute("entities");
  188. attr.Value = scene.Entities.Count.ToString();
  189. docElement.Attributes.Append(attr);
  190. //attr = basedoc.CreateAttribute("parcels");
  191. //attr.Value = scene.LandManager.landList.Count.ToString();
  192. //docElement.Attributes.Append(attr);
  193. XmlNode infoblock = basedoc.CreateNode(XmlNodeType.Element, "info", "");
  194. XmlNode infopiece = basedoc.CreateNode(XmlNodeType.Element, "uuid", "");
  195. infopiece.InnerText = scene.RegionInfo.RegionID.ToString();
  196. infoblock.AppendChild(infopiece);
  197. infopiece = basedoc.CreateNode(XmlNodeType.Element, "url", "");
  198. infopiece.InnerText = "http://" + m_hostname + ":" + m_listener_port;
  199. infoblock.AppendChild(infopiece);
  200. infopiece = basedoc.CreateNode(XmlNodeType.Element, "name", "");
  201. infopiece.InnerText = scene.RegionInfo.RegionName;
  202. infoblock.AppendChild(infopiece);
  203. infopiece = basedoc.CreateNode(XmlNodeType.Element, "handle", "");
  204. infopiece.InnerText = scene.RegionInfo.RegionHandle.ToString();
  205. infoblock.AppendChild(infopiece);
  206. docElement.AppendChild(infoblock);
  207. m_log.Debug("[DATASNAPSHOT]: Generated region node");
  208. return docElement;
  209. }
  210. private String GetRegionCategory(Scene scene)
  211. {
  212. //Boolean choice between:
  213. // "PG" - Mormontown
  214. // "Mature" - Sodom and Gomorrah
  215. if (scene.RegionInfo.RegionSettings.Maturity == 1)
  216. {
  217. return "Mature";
  218. }
  219. else if (scene.RegionInfo.RegionSettings.Maturity == 0)
  220. {
  221. return "PG";
  222. }
  223. else
  224. {
  225. return "Unknown";
  226. }
  227. }
  228. private XmlNode GetGridSnapshotData(XmlDocument factory)
  229. {
  230. XmlNode griddata = factory.CreateNode(XmlNodeType.Element, "grid", "");
  231. foreach (KeyValuePair<String, String> GridData in m_gridinfo)
  232. {
  233. //TODO: make it lowercase tag names for diva
  234. XmlNode childnode = factory.CreateNode(XmlNodeType.Element, GridData.Key, "");
  235. childnode.InnerText = GridData.Value;
  236. griddata.AppendChild(childnode);
  237. }
  238. m_log.Debug("[DATASNAPSHOT]: Got grid snapshot data");
  239. return griddata;
  240. }
  241. #endregion
  242. #region Manage internal collections
  243. public void AddScene(Scene newScene)
  244. {
  245. m_scenes.Add(newScene, true);
  246. }
  247. public void RemoveScene(Scene deadScene)
  248. {
  249. m_scenes.Remove(deadScene);
  250. }
  251. public void AddProvider(IDataSnapshotProvider newProvider)
  252. {
  253. m_providers.Add(newProvider);
  254. }
  255. public void RemoveProvider(IDataSnapshotProvider deadProvider)
  256. {
  257. m_providers.Remove(deadProvider);
  258. }
  259. #endregion
  260. }
  261. }