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