SnapshotStore.cs 13 KB

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