diff options
author | Justin Clark-Casey (justincc) | 2014-10-20 23:46:34 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2014-11-25 23:21:38 +0000 |
commit | 1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8 (patch) | |
tree | f8abd87d29a07bb6baf3c82d2824ff070a316aaa | |
parent | Add regression test TestPostAssetRewrite() to check results of HGAssetMapper.... (diff) | |
download | opensim-SC-1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8.zip opensim-SC-1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8.tar.gz opensim-SC-1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8.tar.bz2 opensim-SC-1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8.tar.xz |
When inserting missing CreatorData in the HGAssetMapper, do the rewrite on a streaming xml basis rather than loading it all into memory via XmlDocument.
This is because objects with lots of parts can have a lot of xml to load into memory, and this has been seen to have a noticeable performance impact.
Whereas streaming has been seen to reduce the impact in normal serialization.
Implmentation is messy but I couldn't see a better way of doing it when you can't assume that you know the exact structure of the input XML.
-rw-r--r-- | OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs | 215 | ||||
-rw-r--r-- | OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs | 1 |
2 files changed, 185 insertions, 31 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs index 04615a9..2ac1517 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs | |||
@@ -189,50 +189,203 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
189 | return Utils.StringToBytes(RewriteSOP(xml)); | 189 | return Utils.StringToBytes(RewriteSOP(xml)); |
190 | } | 190 | } |
191 | 191 | ||
192 | protected string RewriteSOP(string xml) | 192 | protected void TransformXml(XmlReader reader, XmlWriter writer) |
193 | { | 193 | { |
194 | XmlDocument doc = new XmlDocument(); | 194 | // m_log.DebugFormat("[HG ASSET MAPPER]: Transforming XML"); |
195 | doc.LoadXml(xml); | ||
196 | XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart"); | ||
197 | 195 | ||
198 | foreach (XmlNode sop in sops) | 196 | int sopDepth = -1; |
197 | UserAccount creator = null; | ||
198 | bool hasCreatorData = false; | ||
199 | |||
200 | while (reader.Read()) | ||
199 | { | 201 | { |
200 | UserAccount creator = null; | 202 | //Console.WriteLine("Depth: {0}", reader.Depth); |
201 | bool hasCreatorData = false; | 203 | |
202 | XmlNodeList nodes = sop.ChildNodes; | 204 | switch (reader.NodeType) |
203 | foreach (XmlNode node in nodes) | ||
204 | { | 205 | { |
205 | if (node.Name == "CreatorID") | 206 | case XmlNodeType.Attribute: |
207 | writer.WriteAttributeString(reader.Prefix, reader.Name, reader.NamespaceURI, reader.Value); | ||
208 | break; | ||
209 | |||
210 | case XmlNodeType.CDATA: | ||
211 | writer.WriteCData(reader.Value); | ||
212 | break; | ||
213 | |||
214 | case XmlNodeType.Comment: | ||
215 | writer.WriteComment(reader.Value); | ||
216 | break; | ||
217 | |||
218 | case XmlNodeType.DocumentType: | ||
219 | writer.WriteDocType(reader.Name, reader.Value, null, null); | ||
220 | break; | ||
221 | |||
222 | case XmlNodeType.Element: | ||
223 | // m_log.DebugFormat("Depth {0} at element {1}", reader.Depth, reader.Name); | ||
224 | |||
225 | writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); | ||
226 | |||
227 | if (reader.LocalName == "SceneObjectPart") | ||
206 | { | 228 | { |
207 | UUID uuid = UUID.Zero; | 229 | if (sopDepth < 0) |
208 | UUID.TryParse(node.InnerText, out uuid); | 230 | { |
209 | creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid); | 231 | sopDepth = reader.Depth; |
232 | // m_log.DebugFormat("[HG ASSET MAPPER]: Set sopDepth to {0}", sopDepth); | ||
233 | } | ||
234 | } | ||
235 | else | ||
236 | { | ||
237 | if (sopDepth >= 0 && reader.Depth == sopDepth + 1) | ||
238 | { | ||
239 | if (reader.Name == "CreatorID") | ||
240 | { | ||
241 | reader.Read(); | ||
242 | if (reader.NodeType == XmlNodeType.Element && reader.Name == "Guid" || reader.Name == "UUID") | ||
243 | { | ||
244 | reader.Read(); | ||
245 | |||
246 | if (reader.NodeType == XmlNodeType.Text) | ||
247 | { | ||
248 | UUID uuid = UUID.Zero; | ||
249 | UUID.TryParse(reader.Value, out uuid); | ||
250 | creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid); | ||
251 | writer.WriteElementString("UUID", reader.Value); | ||
252 | reader.Read(); | ||
253 | } | ||
254 | else | ||
255 | { | ||
256 | // If we unexpected run across mixed content in this node, still carry on | ||
257 | // transforming the subtree (this replicates earlier behaviour). | ||
258 | TransformXml(reader, writer); | ||
259 | } | ||
260 | } | ||
261 | else | ||
262 | { | ||
263 | // If we unexpected run across mixed content in this node, still carry on | ||
264 | // transforming the subtree (this replicates earlier behaviour). | ||
265 | TransformXml(reader, writer); | ||
266 | } | ||
267 | } | ||
268 | else if (reader.Name == "CreatorData") | ||
269 | { | ||
270 | reader.Read(); | ||
271 | if (reader.NodeType == XmlNodeType.Text) | ||
272 | { | ||
273 | hasCreatorData = true; | ||
274 | writer.WriteString(reader.Value); | ||
275 | } | ||
276 | else | ||
277 | { | ||
278 | // If we unexpected run across mixed content in this node, still carry on | ||
279 | // transforming the subtree (this replicates earlier behaviour). | ||
280 | TransformXml(reader, writer); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | if (reader.IsEmptyElement) | ||
287 | { | ||
288 | // m_log.DebugFormat("[HG ASSET MAPPER]: Writing end for empty element {0}", reader.Name); | ||
289 | writer.WriteEndElement(); | ||
210 | } | 290 | } |
211 | if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty) | ||
212 | hasCreatorData = true; | ||
213 | |||
214 | //if (node.Name == "OwnerID") | ||
215 | //{ | ||
216 | // UserAccount owner = GetUser(node.InnerText); | ||
217 | // if (owner != null) | ||
218 | // node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName; | ||
219 | //} | ||
220 | } | ||
221 | 291 | ||
222 | if (!hasCreatorData && creator != null) | 292 | break; |
223 | { | 293 | |
224 | XmlElement creatorData = doc.CreateElement("CreatorData"); | 294 | case XmlNodeType.EndElement: |
225 | creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName; | 295 | // m_log.DebugFormat("Depth {0} at EndElement", reader.Depth); |
226 | sop.AppendChild(creatorData); | 296 | if (sopDepth == reader.Depth) |
297 | { | ||
298 | if (!hasCreatorData && creator != null) | ||
299 | writer.WriteElementString(reader.Prefix, "CreatorData", reader.NamespaceURI, string.Format("{0};{1} {2}", m_HomeURI, creator.FirstName, creator.LastName)); | ||
300 | |||
301 | // m_log.DebugFormat("[HG ASSET MAPPER]: Reset sopDepth"); | ||
302 | sopDepth = -1; | ||
303 | creator = null; | ||
304 | hasCreatorData = false; | ||
305 | } | ||
306 | writer.WriteEndElement(); | ||
307 | break; | ||
308 | |||
309 | case XmlNodeType.EntityReference: | ||
310 | writer.WriteEntityRef(reader.Name); | ||
311 | break; | ||
312 | |||
313 | case XmlNodeType.ProcessingInstruction: | ||
314 | writer.WriteProcessingInstruction(reader.Name, reader.Value); | ||
315 | break; | ||
316 | |||
317 | case XmlNodeType.Text: | ||
318 | writer.WriteString(reader.Value); | ||
319 | break; | ||
320 | |||
321 | default: | ||
322 | m_log.WarnFormat("[HG ASSET MAPPER]: Unrecognized node in asset XML transform in {0}", m_scene.Name); | ||
323 | break; | ||
227 | } | 324 | } |
228 | } | 325 | } |
326 | } | ||
327 | |||
328 | protected string RewriteSOP(string xmlData) | ||
329 | { | ||
330 | // Console.WriteLine("Input XML [{0}]", xmlData); | ||
229 | 331 | ||
230 | using (StringWriter wr = new StringWriter()) | 332 | using (StringWriter sw = new StringWriter()) |
333 | using (XmlTextWriter writer = new XmlTextWriter(sw)) | ||
334 | using (XmlTextReader wrappedReader = new XmlTextReader(xmlData, XmlNodeType.Element, null)) | ||
335 | using (XmlReader reader = XmlReader.Create(wrappedReader, new XmlReaderSettings() { IgnoreWhitespace = true, ConformanceLevel = ConformanceLevel.Fragment })) | ||
231 | { | 336 | { |
232 | doc.Save(wr); | 337 | TransformXml(reader, writer); |
233 | return wr.ToString(); | 338 | |
339 | writer.WriteEndDocument(); | ||
340 | |||
341 | // Console.WriteLine("Output: [{0}]", sw.ToString()); | ||
342 | |||
343 | return sw.ToString(); | ||
234 | } | 344 | } |
235 | 345 | ||
346 | // We are now taking the more complex streaming approach above because some assets can be very large | ||
347 | // and can trigger higher CPU use or possibly memory problems. | ||
348 | // XmlDocument doc = new XmlDocument(); | ||
349 | // doc.LoadXml(xml); | ||
350 | // XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart"); | ||
351 | // | ||
352 | // foreach (XmlNode sop in sops) | ||
353 | // { | ||
354 | // UserAccount creator = null; | ||
355 | // bool hasCreatorData = false; | ||
356 | // XmlNodeList nodes = sop.ChildNodes; | ||
357 | // foreach (XmlNode node in nodes) | ||
358 | // { | ||
359 | // if (node.Name == "CreatorID") | ||
360 | // { | ||
361 | // UUID uuid = UUID.Zero; | ||
362 | // UUID.TryParse(node.InnerText, out uuid); | ||
363 | // creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid); | ||
364 | // } | ||
365 | // if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty) | ||
366 | // hasCreatorData = true; | ||
367 | // | ||
368 | // //if (node.Name == "OwnerID") | ||
369 | // //{ | ||
370 | // // UserAccount owner = GetUser(node.InnerText); | ||
371 | // // if (owner != null) | ||
372 | // // node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName; | ||
373 | // //} | ||
374 | // } | ||
375 | // | ||
376 | // if (!hasCreatorData && creator != null) | ||
377 | // { | ||
378 | // XmlElement creatorData = doc.CreateElement("CreatorData"); | ||
379 | // creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName; | ||
380 | // sop.AppendChild(creatorData); | ||
381 | // } | ||
382 | // } | ||
383 | // | ||
384 | // using (StringWriter wr = new StringWriter()) | ||
385 | // { | ||
386 | // doc.Save(wr); | ||
387 | // return wr.ToString(); | ||
388 | // } | ||
236 | } | 389 | } |
237 | 390 | ||
238 | // TODO: unused | 391 | // TODO: unused |
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs index 3dbddfb..779da43 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs | |||
@@ -44,6 +44,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests | |||
44 | public void TestPostAssetRewrite() | 44 | public void TestPostAssetRewrite() |
45 | { | 45 | { |
46 | TestHelpers.InMethod(); | 46 | TestHelpers.InMethod(); |
47 | // TestHelpers.EnableLogging(); | ||
47 | 48 | ||
48 | string homeUrl = "http://hg.HomeTestPostAssetRewriteGrid.com"; | 49 | string homeUrl = "http://hg.HomeTestPostAssetRewriteGrid.com"; |
49 | string foreignUrl = "http://hg.ForeignTestPostAssetRewriteGrid.com"; | 50 | string foreignUrl = "http://hg.ForeignTestPostAssetRewriteGrid.com"; |