From 74a2bd237aed26b49d88045fe6afa526ed19c00d Mon Sep 17 00:00:00 2001
From: Mike Mazur
Date: Mon, 16 Feb 2009 02:26:27 +0000
Subject: Add OpenSim & Simple inventory storage plugins and Null metrics
plugin.
---
.../AssetInventoryServerSimplePlugins.addin.xml | 3 +
.../Plugins/Simple/SimpleInventoryStoragePlugin.cs | 627 +++++++++++++++++++++
2 files changed, 630 insertions(+)
create mode 100644 OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs
(limited to 'OpenSim/Grid/AssetInventoryServer/Plugins/Simple')
diff --git a/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/Resources/AssetInventoryServerSimplePlugins.addin.xml b/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/Resources/AssetInventoryServerSimplePlugins.addin.xml
index 53534c4..f898145 100644
--- a/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/Resources/AssetInventoryServerSimplePlugins.addin.xml
+++ b/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/Resources/AssetInventoryServerSimplePlugins.addin.xml
@@ -10,4 +10,7 @@
+
+
+
diff --git a/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs b/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs
new file mode 100644
index 0000000..78dae35
--- /dev/null
+++ b/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2008 Intel Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * -- Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * -- Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * -- Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.IO;
+using System.Text;
+using ExtensionLoader;
+using OpenMetaverse;
+using OpenMetaverse.StructuredData;
+using OpenSim.Framework;
+
+namespace OpenSim.Grid.AssetInventoryServer.Plugins.Simple
+{
+ public class SimpleInventoryStoragePlugin : IInventoryStorageProvider
+ {
+ const string EXTENSION_NAME = "SimpleInventoryStorage"; // Used for metrics reporting
+ const string DEFAULT_INVENTORY_DIR = "SimpleInventory";
+
+ AssetInventoryServer server;
+ Dictionary inventories = new Dictionary();
+ Dictionary> activeGestures = new Dictionary>();
+ Utils.InventoryItemSerializer itemSerializer = new Utils.InventoryItemSerializer();
+ Utils.InventoryFolderSerializer folderSerializer = new Utils.InventoryFolderSerializer();
+
+ public SimpleInventoryStoragePlugin()
+ {
+ }
+
+ #region Required Interfaces
+
+ public BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItem item)
+ {
+ item = null;
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
+ ret = BackendResponse.Success;
+ else
+ ret = BackendResponse.NotFound;
+
+ server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolder folder)
+ {
+ folder = null;
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
+ ret = BackendResponse.Success;
+ else
+ ret = BackendResponse.NotFound;
+
+ server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents)
+ {
+ contents = null;
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ InventoryFolder folder;
+
+ if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
+ {
+ contents = new InventoryCollection();
+ contents.UserID = collection.UserID;
+ contents.Folders = new Dictionary();
+ contents.Items = new Dictionary();
+
+ foreach (InventoryBase invBase in folder.Children.Values)
+ {
+ if (invBase is InventoryItem)
+ {
+ InventoryItem invItem = invBase as InventoryItem;
+ contents.Items.Add(invItem.ID, invItem);
+ }
+ else
+ {
+ InventoryFolder invFolder = invBase as InventoryFolder;
+ contents.Folders.Add(invFolder.ID, invFolder);
+ }
+ }
+
+ ret = BackendResponse.Success;
+ }
+ else
+ {
+ ret = BackendResponse.NotFound;
+ }
+
+ server.MetricsProvider.LogInventoryFetchFolderContents(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryFetchFolderList(Uri owner, out List folders)
+ {
+ folders = null;
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ if (inventories.TryGetValue(owner, out collection))
+ {
+ folders = new List(collection.Folders.Values);
+ return BackendResponse.Success;
+ }
+ else
+ {
+ ret = BackendResponse.NotFound;
+ }
+
+ server.MetricsProvider.LogInventoryFetchFolderList(EXTENSION_NAME, ret, owner, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory)
+ {
+ inventory = null;
+ BackendResponse ret;
+
+ if (inventories.TryGetValue(owner, out inventory))
+ ret = BackendResponse.Success;
+ else
+ ret = BackendResponse.NotFound;
+
+ server.MetricsProvider.LogInventoryFetchInventory(EXTENSION_NAME, ret, owner, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryFetchActiveGestures(Uri owner, out List gestures)
+ {
+ gestures = null;
+ BackendResponse ret;
+
+ if (activeGestures.TryGetValue(owner, out gestures))
+ ret = BackendResponse.Success;
+ else
+ ret = BackendResponse.NotFound;
+
+ server.MetricsProvider.LogInventoryFetchActiveGestures(EXTENSION_NAME, ret, owner, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryCreateItem(Uri owner, InventoryItem item)
+ {
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ if (inventories.TryGetValue(owner, out collection))
+ {
+ // Delete this item first if it already exists
+ InventoryItem oldItem;
+ if (collection.Items.TryGetValue(item.ID, out oldItem))
+ TryDeleteItem(owner, item.ID);
+
+ try
+ {
+ // Create the file
+ SaveItem(item);
+
+ // Add the item to the collection
+ lock (collection) collection.Items[item.ID] = item;
+
+ // Add the item to its parent folder
+ InventoryFolder parent;
+ if (collection.Folders.TryGetValue(item.Folder, out parent))
+ lock (parent.Children) parent.Children.Add(item.ID, item);
+
+ // Add active gestures to our list
+ if (item.InvType == (int)InventoryType.Gesture && item.Flags == 1)
+ {
+ lock (activeGestures)
+ activeGestures[owner].Add(item);
+ }
+
+ ret = BackendResponse.Success;
+ }
+ catch (Exception ex)
+ {
+ Logger.Log.Error(ex.Message);
+ ret = BackendResponse.Failure;
+ }
+ }
+ else
+ {
+ return BackendResponse.NotFound;
+ }
+
+ server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, false, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryCreateFolder(Uri owner, InventoryFolder folder)
+ {
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ if (inventories.TryGetValue(owner, out collection))
+ {
+ // Delete this folder first if it already exists
+ InventoryFolder oldFolder;
+ if (collection.Folders.TryGetValue(folder.ID, out oldFolder))
+ TryDeleteFolder(owner, folder.ID);
+
+ try
+ {
+ // Create the file
+ SaveFolder(folder);
+
+ // Add the folder to the collection
+ lock (collection) collection.Folders[folder.ID] = folder;
+
+ // Add the folder to its parent folder
+ InventoryFolder parent;
+ if (collection.Folders.TryGetValue(folder.ParentID, out parent))
+ lock (parent.Children) parent.Children.Add(folder.ID, folder);
+
+ ret = BackendResponse.Success;
+ }
+ catch (Exception ex)
+ {
+ Logger.Log.Error(ex.Message);
+ ret = BackendResponse.Failure;
+ }
+ }
+ else
+ {
+ ret = BackendResponse.NotFound;
+ }
+
+ server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, true, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryCreateInventory(Uri owner, InventoryFolder rootFolder)
+ {
+ BackendResponse ret;
+
+ lock (inventories)
+ {
+ if (!inventories.ContainsKey(owner))
+ {
+ InventoryCollection collection = new InventoryCollection();
+ collection.UserID = rootFolder.Owner;
+ collection.Folders = new Dictionary();
+ collection.Folders.Add(rootFolder.ID, rootFolder);
+ collection.Items = new Dictionary();
+
+ inventories.Add(owner, collection);
+
+ ret = BackendResponse.Success;
+ }
+ else
+ {
+ ret = BackendResponse.Failure;
+ }
+ }
+
+ if (ret == BackendResponse.Success)
+ {
+ string path = Path.Combine(DEFAULT_INVENTORY_DIR, rootFolder.Owner.ToString());
+ try
+ {
+ // Create the directory for this agent
+ Directory.CreateDirectory(path);
+
+ // Create an index.txt containing the UUID and URI for this agent
+ string[] index = new string[] { rootFolder.Owner.ToString(), owner.ToString() };
+ File.WriteAllLines(Path.Combine(path, "index.txt"), index);
+
+ // Create the root folder file
+ SaveFolder(rootFolder);
+ }
+ catch (Exception ex)
+ {
+ Logger.Log.Error(ex.Message);
+ ret = BackendResponse.Failure;
+ }
+ }
+
+ server.MetricsProvider.LogInventoryCreateInventory(EXTENSION_NAME, ret, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryDeleteItem(Uri owner, UUID itemID)
+ {
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ InventoryItem item;
+ if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
+ {
+ // Remove the item from its parent folder
+ InventoryFolder parent;
+ if (collection.Folders.TryGetValue(item.Folder, out parent))
+ lock (parent.Children) parent.Children.Remove(itemID);
+
+ // Remove the item from the collection
+ lock (collection) collection.Items.Remove(itemID);
+
+ // Remove from the active gestures list if applicable
+ if (item.InvType == (int)InventoryType.Gesture)
+ {
+ lock (activeGestures)
+ {
+ for (int i = 0; i < activeGestures[owner].Count; i++)
+ {
+ if (activeGestures[owner][i].ID == itemID)
+ {
+ activeGestures[owner].RemoveAt(i);
+ break;
+ }
+ }
+ }
+ }
+
+ // Delete the file. We don't know exactly what the file name is,
+ // so search for it
+ string path = PathFromURI(owner);
+ string[] matches = Directory.GetFiles(path, String.Format("*{0}.item", itemID), SearchOption.TopDirectoryOnly);
+ foreach (string match in matches)
+ {
+ try { File.Delete(match); }
+ catch (Exception ex) { Logger.Log.ErrorFormat("Failed to delete file {0}: {1}", match, ex.Message); }
+ }
+
+ ret = BackendResponse.Success;
+ }
+ else
+ {
+ ret = BackendResponse.NotFound;
+ }
+
+ server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryDeleteFolder(Uri owner, UUID folderID)
+ {
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ InventoryFolder folder;
+ if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
+ {
+ // Remove the folder from its parent folder
+ InventoryFolder parent;
+ if (collection.Folders.TryGetValue(folder.ParentID, out parent))
+ lock (parent.Children) parent.Children.Remove(folderID);
+
+ // Remove the folder from the collection
+ lock (collection) collection.Items.Remove(folderID);
+
+ // Delete the folder file. We don't know exactly what the file name is,
+ // so search for it
+ string path = PathFromURI(owner);
+ string[] matches = Directory.GetFiles(path, String.Format("*{0}.folder", folderID), SearchOption.TopDirectoryOnly);
+ foreach (string match in matches)
+ {
+ try { File.Delete(match); }
+ catch (Exception ex) { Logger.Log.ErrorFormat("Failed to delete folder file {0}: {1}", match, ex.Message); }
+ }
+
+ ret = BackendResponse.Success;
+ }
+ else
+ {
+ ret = BackendResponse.NotFound;
+ }
+
+ server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
+ return ret;
+ }
+
+ public BackendResponse TryPurgeFolder(Uri owner, UUID folderID)
+ {
+ BackendResponse ret;
+
+ InventoryCollection collection;
+ InventoryFolder folder;
+ if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
+ {
+ // Delete all of the folder children
+ foreach (InventoryBase obj in new List(folder.Children.Values))
+ {
+ if (obj is InventoryItem)
+ {
+ TryDeleteItem(owner, (obj as InventoryItem).ID);
+ }
+ else
+ {
+ InventoryFolder childFolder = obj as InventoryFolder;
+ TryPurgeFolder(owner, childFolder.ID);
+ TryDeleteFolder(owner, childFolder.ID);
+ }
+ }
+
+ ret = BackendResponse.Success;
+ }
+ else
+ {
+ ret = BackendResponse.NotFound;
+ }
+
+ server.MetricsProvider.LogInventoryPurgeFolder(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
+ return ret;
+ }
+
+ #endregion Required Interfaces
+
+ void SaveItem(InventoryItem item)
+ {
+ string filename = String.Format("{0}-{1}.item", SanitizeFilename(item.Name), item.ID);
+
+ string path = Path.Combine(DEFAULT_INVENTORY_DIR, item.Owner.ToString());
+ path = Path.Combine(path, filename);
+
+ using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
+ {
+ itemSerializer.Serialize(stream, item);
+ stream.Flush();
+ }
+ }
+
+ void SaveFolder(InventoryFolder folder)
+ {
+ string filename = String.Format("{0}-{1}.folder", SanitizeFilename(folder.Name), folder.ID);
+
+ string path = Path.Combine(DEFAULT_INVENTORY_DIR, folder.Owner.ToString());
+ path = Path.Combine(path, filename);
+
+ using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
+ {
+ folderSerializer.Serialize(stream, folder);
+ stream.Flush();
+ }
+ }
+
+ string SanitizeFilename(string filename)
+ {
+ string output = filename;
+
+ if (output.Length > 64)
+ output = output.Substring(0, 64);
+
+ foreach (char i in Path.GetInvalidFileNameChars())
+ output = output.Replace(i, '_');
+
+ return output;
+ }
+
+ static string PathFromURI(Uri uri)
+ {
+ byte[] hash = OpenMetaverse.Utils.SHA1(Encoding.UTF8.GetBytes(uri.ToString()));
+ StringBuilder digest = new StringBuilder(40);
+
+ // Convert the hash to a hex string
+ foreach (byte b in hash)
+ digest.AppendFormat(OpenMetaverse.Utils.EnUsCulture, "{0:x2}", b);
+
+ return Path.Combine(DEFAULT_INVENTORY_DIR, digest.ToString());
+ }
+
+ void LoadFiles(string folder)
+ {
+ // Try to create the directory if it doesn't already exist
+ if (!Directory.Exists(folder))
+ {
+ try { Directory.CreateDirectory(folder); }
+ catch (Exception ex)
+ {
+ Logger.Log.Warn(ex.Message);
+ return;
+ }
+ }
+
+ try
+ {
+ string[] agentFolders = Directory.GetDirectories(DEFAULT_INVENTORY_DIR);
+
+ for (int i = 0; i < agentFolders.Length; i++)
+ {
+ string foldername = agentFolders[i];
+ string indexPath = Path.Combine(foldername, "index.txt");
+ UUID ownerID = UUID.Zero;
+ Uri owner = null;
+
+ try
+ {
+ string[] index = File.ReadAllLines(indexPath);
+ ownerID = UUID.Parse(index[0]);
+ owner = new Uri(index[1]);
+ }
+ catch (Exception ex)
+ {
+ Logger.Log.WarnFormat("Failed loading the index file {0}: {1}", indexPath, ex.Message);
+ }
+
+ if (ownerID != UUID.Zero && owner != null)
+ {
+ // Initialize the active gestures list for this agent
+ activeGestures.Add(owner, new List());
+
+ InventoryCollection collection = new InventoryCollection();
+ collection.UserID = ownerID;
+
+ // Load all of the folders for this agent
+ string[] folders = Directory.GetFiles(foldername, "*.folder", SearchOption.TopDirectoryOnly);
+ collection.Folders = new Dictionary(folders.Length);
+
+ for (int j = 0; j < folders.Length; j++)
+ {
+ InventoryFolder invFolder = (InventoryFolder)folderSerializer.Deserialize(
+ new FileStream(folders[j], FileMode.Open, FileAccess.Read));
+ collection.Folders[invFolder.ID] = invFolder;
+ }
+
+ // Iterate over the folders collection, adding children to their parents
+ foreach (InventoryFolder invFolder in collection.Folders.Values)
+ {
+ InventoryFolder parent;
+ if (collection.Folders.TryGetValue(invFolder.ParentID, out parent))
+ parent.Children[invFolder.ID] = invFolder;
+ }
+
+ // Load all of the items for this agent
+ string[] files = Directory.GetFiles(foldername, "*.item", SearchOption.TopDirectoryOnly);
+ collection.Items = new Dictionary(files.Length);
+
+ for (int j = 0; j < files.Length; j++)
+ {
+ InventoryItem invItem = (InventoryItem)itemSerializer.Deserialize(
+ new FileStream(files[j], FileMode.Open, FileAccess.Read));
+ collection.Items[invItem.ID] = invItem;
+
+ // Add items to their parent folders
+ InventoryFolder parent;
+ if (collection.Folders.TryGetValue(invItem.Folder, out parent))
+ parent.Children[invItem.ID] = invItem;
+
+ // Add active gestures to our list
+ if (invItem.InvType == (int)InventoryType.Gesture && invItem.Flags != 0)
+ activeGestures[owner].Add(invItem);
+ }
+
+ inventories.Add(owner, collection);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Log.ErrorFormat("Failed loading inventory from {0}: {1}", folder, ex.Message);
+ }
+ }
+
+ #region IPlugin implementation
+
+ public void Initialise(AssetInventoryServer server)
+ {
+ this.server = server;
+
+ LoadFiles(DEFAULT_INVENTORY_DIR);
+
+ Logger.Log.InfoFormat("Initialized the inventory index with data for {0} avatars",
+ inventories.Count);
+ }
+
+ ///
+ /// Initialises asset interface
+ ///
+ public void Initialise()
+ {
+ Logger.Log.InfoFormat("[ASSET]: {0} cannot be default-initialized!", Name);
+ throw new PluginNotInitialisedException(Name);
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public string Version
+ {
+ // TODO: this should be something meaningful and not hardcoded?
+ get { return "0.1"; }
+ }
+
+ public string Name
+ {
+ get { return "AssetInventoryServer Simple inventory storage provider"; }
+ }
+
+ #endregion IPlugin implementation
+ }
+}
--
cgit v1.1