diff --git a/OpenSim/Framework/InventoryFolderImpl.cs b/OpenSim/Framework/InventoryFolderImpl.cs index 248783fa4f..00462f918d 100644 --- a/OpenSim/Framework/InventoryFolderImpl.cs +++ b/OpenSim/Framework/InventoryFolderImpl.cs @@ -1,466 +1,466 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * 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 OpenSimulator Project 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 DEVELOPERS ``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 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 OpenMetaverse; - -namespace OpenSim.Framework -{ - public class InventoryFolderImpl : InventoryFolderBase - { - //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - public static readonly string PATH_DELIMITER = "/"; - - /// - /// Items that are contained in this folder - /// - public Dictionary Items = new Dictionary(); - - /// - /// Child folders that are contained in this folder - /// - protected Dictionary m_childFolders = new Dictionary(); - - // Constructors - public InventoryFolderImpl(InventoryFolderBase folderbase) - { - Owner = folderbase.Owner; - ID = folderbase.ID; - Name = folderbase.Name; - ParentID = folderbase.ParentID; - Type = folderbase.Type; - Version = folderbase.Version; - } - - public InventoryFolderImpl() - { - } - - /// - /// Create a new subfolder. - /// - /// - /// - /// - /// The newly created subfolder. Returns null if the folder already exists - public InventoryFolderImpl CreateChildFolder(UUID folderID, string folderName, ushort type) - { - lock (m_childFolders) - { - if (!m_childFolders.ContainsKey(folderID)) - { - InventoryFolderImpl subFold = new InventoryFolderImpl(); - subFold.Name = folderName; - subFold.ID = folderID; - subFold.Type = (short)type; - subFold.ParentID = this.ID; - subFold.Owner = Owner; - m_childFolders.Add(subFold.ID, subFold); - - return subFold; - } - } - - return null; - } - - /// - /// Add a folder that already exists. - /// - /// - public void AddChildFolder(InventoryFolderImpl folder) - { - lock (m_childFolders) - { - folder.ParentID = ID; - m_childFolders[folder.ID] = folder; - } - } - - /// - /// Does this folder contain the given child folder? - /// - /// - /// - public bool ContainsChildFolder(UUID folderID) - { - return m_childFolders.ContainsKey(folderID); - } - - /// - /// Get a child folder - /// - /// - /// The folder if it exists, null if it doesn't - public InventoryFolderImpl GetChildFolder(UUID folderID) - { - InventoryFolderImpl folder = null; - - lock (m_childFolders) - { - m_childFolders.TryGetValue(folderID, out folder); - } - - return folder; - } - - /// - /// Removes the given child subfolder. - /// - /// - /// - /// The folder removed, or null if the folder was not present. - /// - public InventoryFolderImpl RemoveChildFolder(UUID folderID) - { - InventoryFolderImpl removedFolder = null; - - lock (m_childFolders) - { - if (m_childFolders.ContainsKey(folderID)) - { - removedFolder = m_childFolders[folderID]; - m_childFolders.Remove(folderID); - } - } - - return removedFolder; - } - - /// - /// Delete all the folders and items in this folder. - /// - public void Purge() - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - folder.Purge(); - } - - m_childFolders.Clear(); - Items.Clear(); - } - - /// - /// Returns the item if it exists in this folder or in any of this folder's descendant folders - /// - /// - /// null if the item is not found - public InventoryItemBase FindItem(UUID itemID) - { - lock (Items) - { - if (Items.ContainsKey(itemID)) - { - return Items[itemID]; - } - } - - lock (m_childFolders) - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - InventoryItemBase item = folder.FindItem(itemID); - - if (item != null) - { - return item; - } - } - } - - return null; - } - - public InventoryItemBase FindAsset(UUID assetID) - { - lock (Items) - { - foreach (InventoryItemBase item in Items.Values) - { - if (item.AssetID == assetID) - return item; - } - } - - lock (m_childFolders) - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - InventoryItemBase item = folder.FindAsset(assetID); - - if (item != null) - { - return item; - } - } - } - - return null; - } - - /// - /// Deletes an item if it exists in this folder or any children - /// - /// - /// - public bool DeleteItem(UUID itemID) - { - bool found = false; - - lock (Items) - { - if (Items.ContainsKey(itemID)) - { - Items.Remove(itemID); - return true; - } - } - - lock (m_childFolders) - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - found = folder.DeleteItem(itemID); - - if (found == true) - { - break; - } - } - } - - return found; - } - - /// - /// Returns the folder requested if it is this folder or is a descendent of this folder. The search is depth - /// first. - /// - /// The requested folder if it exists, null if it does not. - public InventoryFolderImpl FindFolder(UUID folderID) - { - if (folderID == ID) - return this; - - lock (m_childFolders) - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - InventoryFolderImpl returnFolder = folder.FindFolder(folderID); - - if (returnFolder != null) - return returnFolder; - } - } - - return null; - } - - /// - /// Look through all child subfolders for a folder marked as one for a particular asset type, and return it. - /// - /// - /// Returns null if no such folder is found - public InventoryFolderImpl FindFolderForType(int type) - { - lock (m_childFolders) - { - foreach (InventoryFolderImpl f in m_childFolders.Values) - { - if (f.Type == type) - return f; - } - } - - return null; - } - - /// - /// Find a folder given a PATH_DELIMITER delimited path starting from this folder - /// - /// - /// This method does not handle paths that contain multiple delimitors - /// - /// FIXME: We do not yet handle situations where folders have the same name. We could handle this by some - /// XPath like expression - /// - /// FIXME: Delimitors which occur in names themselves are not currently escapable. - /// - /// - /// The path to the required folder. - /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. - /// - /// null if the folder is not found - public InventoryFolderImpl FindFolderByPath(string path) - { - if (path == string.Empty) - return this; - - path = path.Trim(); - - if (path == PATH_DELIMITER) - return this; - - string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); - - lock (m_childFolders) - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - if (folder.Name == components[0]) - if (components.Length > 1) - return folder.FindFolderByPath(components[1]); - else - return folder; - } - } - - // We didn't find a folder with the given name - return null; - } - - /// - /// Find an item given a PATH_DELIMITOR delimited path starting from this folder. - /// - /// This method does not handle paths that contain multiple delimitors - /// - /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some - /// XPath like expression - /// - /// FIXME: Delimitors which occur in names themselves are not currently escapable. - /// - /// - /// The path to the required item. - /// - /// null if the item is not found - public InventoryItemBase FindItemByPath(string path) - { - string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); - - if (components.Length == 1) - { - lock (Items) - { - foreach (InventoryItemBase item in Items.Values) - { - if (item.Name == components[0]) - return item; - } - } - } - else - { - lock (m_childFolders) - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - if (folder.Name == components[0]) - return folder.FindItemByPath(components[1]); - } - } - } - - // We didn't find an item or intermediate folder with the given name - return null; - } - - /// - /// Return a copy of the list of child items in this folder. The items themselves are the originals. - /// - public List RequestListOfItems() - { - List itemList = new List(); - - lock (Items) - { - foreach (InventoryItemBase item in Items.Values) - { - itemList.Add(item); - } - } - - //m_log.DebugFormat("[INVENTORY FOLDER IMPL]: Found {0} items", itemList.Count); - - return itemList; - } - - /// - /// Return a copy of the list of child folders in this folder. The folders themselves are the originals. - /// - public List RequestListOfFolders() - { - List folderList = new List(); - - lock (m_childFolders) - { - foreach (InventoryFolderBase folder in m_childFolders.Values) - { - folderList.Add(folder); - } - } - - return folderList; - } - - public List RequestListOfFolderImpls() - { - List folderList = new List(); - - lock (m_childFolders) - { - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - folderList.Add(folder); - } - } - - return folderList; - } - - /// - /// The total number of items in this folder and in the immediate child folders (though not from other - /// descendants). - /// - public int TotalCount - { - get - { - int total = Items.Count; - - foreach (InventoryFolderImpl folder in m_childFolders.Values) - { - total = total + folder.TotalCount; - } - - return total; - } - } - } -} +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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 OpenSimulator Project 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 DEVELOPERS ``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 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public class InventoryFolderImpl : InventoryFolderBase + { + //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly string PATH_DELIMITER = "/"; + + /// + /// Items that are contained in this folder + /// + public Dictionary Items = new Dictionary(); + + /// + /// Child folders that are contained in this folder + /// + protected Dictionary m_childFolders = new Dictionary(); + + // Constructors + public InventoryFolderImpl(InventoryFolderBase folderbase) + { + Owner = folderbase.Owner; + ID = folderbase.ID; + Name = folderbase.Name; + ParentID = folderbase.ParentID; + Type = folderbase.Type; + Version = folderbase.Version; + } + + public InventoryFolderImpl() + { + } + + /// + /// Create a new subfolder. + /// + /// + /// + /// + /// The newly created subfolder. Returns null if the folder already exists + public InventoryFolderImpl CreateChildFolder(UUID folderID, string folderName, ushort type) + { + lock (m_childFolders) + { + if (!m_childFolders.ContainsKey(folderID)) + { + InventoryFolderImpl subFold = new InventoryFolderImpl(); + subFold.Name = folderName; + subFold.ID = folderID; + subFold.Type = (short)type; + subFold.ParentID = this.ID; + subFold.Owner = Owner; + m_childFolders.Add(subFold.ID, subFold); + + return subFold; + } + } + + return null; + } + + /// + /// Add a folder that already exists. + /// + /// + public void AddChildFolder(InventoryFolderImpl folder) + { + lock (m_childFolders) + { + folder.ParentID = ID; + m_childFolders[folder.ID] = folder; + } + } + + /// + /// Does this folder contain the given child folder? + /// + /// + /// + public bool ContainsChildFolder(UUID folderID) + { + return m_childFolders.ContainsKey(folderID); + } + + /// + /// Get a child folder + /// + /// + /// The folder if it exists, null if it doesn't + public InventoryFolderImpl GetChildFolder(UUID folderID) + { + InventoryFolderImpl folder = null; + + lock (m_childFolders) + { + m_childFolders.TryGetValue(folderID, out folder); + } + + return folder; + } + + /// + /// Removes the given child subfolder. + /// + /// + /// + /// The folder removed, or null if the folder was not present. + /// + public InventoryFolderImpl RemoveChildFolder(UUID folderID) + { + InventoryFolderImpl removedFolder = null; + + lock (m_childFolders) + { + if (m_childFolders.ContainsKey(folderID)) + { + removedFolder = m_childFolders[folderID]; + m_childFolders.Remove(folderID); + } + } + + return removedFolder; + } + + /// + /// Delete all the folders and items in this folder. + /// + public void Purge() + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + folder.Purge(); + } + + m_childFolders.Clear(); + Items.Clear(); + } + + /// + /// Returns the item if it exists in this folder or in any of this folder's descendant folders + /// + /// + /// null if the item is not found + public InventoryItemBase FindItem(UUID itemID) + { + lock (Items) + { + if (Items.ContainsKey(itemID)) + { + return Items[itemID]; + } + } + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + InventoryItemBase item = folder.FindItem(itemID); + + if (item != null) + { + return item; + } + } + } + + return null; + } + + public InventoryItemBase FindAsset(UUID assetID) + { + lock (Items) + { + foreach (InventoryItemBase item in Items.Values) + { + if (item.AssetID == assetID) + return item; + } + } + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + InventoryItemBase item = folder.FindAsset(assetID); + + if (item != null) + { + return item; + } + } + } + + return null; + } + + /// + /// Deletes an item if it exists in this folder or any children + /// + /// + /// + public bool DeleteItem(UUID itemID) + { + bool found = false; + + lock (Items) + { + if (Items.ContainsKey(itemID)) + { + Items.Remove(itemID); + return true; + } + } + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + found = folder.DeleteItem(itemID); + + if (found == true) + { + break; + } + } + } + + return found; + } + + /// + /// Returns the folder requested if it is this folder or is a descendent of this folder. The search is depth + /// first. + /// + /// The requested folder if it exists, null if it does not. + public InventoryFolderImpl FindFolder(UUID folderID) + { + if (folderID == ID) + return this; + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + InventoryFolderImpl returnFolder = folder.FindFolder(folderID); + + if (returnFolder != null) + return returnFolder; + } + } + + return null; + } + + /// + /// Look through all child subfolders for a folder marked as one for a particular asset type, and return it. + /// + /// + /// Returns null if no such folder is found + public InventoryFolderImpl FindFolderForType(int type) + { + lock (m_childFolders) + { + foreach (InventoryFolderImpl f in m_childFolders.Values) + { + if (f.Type == type) + return f; + } + } + + return null; + } + + /// + /// Find a folder given a PATH_DELIMITER delimited path starting from this folder + /// + /// + /// This method does not handle paths that contain multiple delimitors + /// + /// FIXME: We do not yet handle situations where folders have the same name. We could handle this by some + /// XPath like expression + /// + /// FIXME: Delimitors which occur in names themselves are not currently escapable. + /// + /// + /// The path to the required folder. + /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. + /// + /// null if the folder is not found + public InventoryFolderImpl FindFolderByPath(string path) + { + if (path == string.Empty) + return this; + + path = path.Trim(); + + if (path == PATH_DELIMITER) + return this; + + string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + if (folder.Name == components[0]) + if (components.Length > 1) + return folder.FindFolderByPath(components[1]); + else + return folder; + } + } + + // We didn't find a folder with the given name + return null; + } + + /// + /// Find an item given a PATH_DELIMITOR delimited path starting from this folder. + /// + /// This method does not handle paths that contain multiple delimitors + /// + /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some + /// XPath like expression + /// + /// FIXME: Delimitors which occur in names themselves are not currently escapable. + /// + /// + /// The path to the required item. + /// + /// null if the item is not found + public InventoryItemBase FindItemByPath(string path) + { + string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); + + if (components.Length == 1) + { + lock (Items) + { + foreach (InventoryItemBase item in Items.Values) + { + if (item.Name == components[0]) + return item; + } + } + } + else + { + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + if (folder.Name == components[0]) + return folder.FindItemByPath(components[1]); + } + } + } + + // We didn't find an item or intermediate folder with the given name + return null; + } + + /// + /// Return a copy of the list of child items in this folder. The items themselves are the originals. + /// + public List RequestListOfItems() + { + List itemList = new List(); + + lock (Items) + { + foreach (InventoryItemBase item in Items.Values) + { + itemList.Add(item); + } + } + + //m_log.DebugFormat("[INVENTORY FOLDER IMPL]: Found {0} items", itemList.Count); + + return itemList; + } + + /// + /// Return a copy of the list of child folders in this folder. The folders themselves are the originals. + /// + public List RequestListOfFolders() + { + List folderList = new List(); + + lock (m_childFolders) + { + foreach (InventoryFolderBase folder in m_childFolders.Values) + { + folderList.Add(folder); + } + } + + return folderList; + } + + public List RequestListOfFolderImpls() + { + List folderList = new List(); + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + folderList.Add(folder); + } + } + + return folderList; + } + + /// + /// The total number of items in this folder and in the immediate child folders (though not from other + /// descendants). + /// + public int TotalCount + { + get + { + int total = Items.Count; + + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + total = total + folder.TotalCount; + } + + return total; + } + } + } +} diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 99282a33c2..9540bd48a7 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -1,523 +1,523 @@ -/* -Copyright (c) Contributors, http://osflotsam.org/ -See CONTRIBUTORS.TXT for a full list of copyright holders. - -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 Flotsam Project 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 DEVELOPERS ``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 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. */ - -// Uncomment to make asset Get requests for existing -// #define WAIT_ON_INPROGRESS_REQUESTS - -using System; -using System.IO; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; -using System.Threading; -using System.Timers; - -using log4net; -using Nini.Config; -using Mono.Addins; -using OpenMetaverse; - -using OpenSim.Framework; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; -using OpenSim.Services.Interfaces; - - -[assembly: Addin("FlotsamAssetCache", "1.1")] -[assembly: AddinDependency("OpenSim", "0.5")] - -namespace Flotsam.RegionModules.AssetCache -{ - /// - /// OpenSim.ini Options: - /// ------- - /// [Modules] - /// AssetCaching = "FlotsamAssetCache" - /// - /// [AssetCache] - /// ; cache directory can be shared by multiple instances - /// CacheDirectory = /directory/writable/by/OpenSim/instance - /// - /// ; Log level - /// ; 0 - (Error) Errors only - /// ; 1 - (Info) Hit Rate Stats + Level 0 - /// ; 2 - (Debug) Cache Activity (Reads/Writes) + Level 1 - /// ; - /// LogLevel = 1 - /// - /// ; How often should hit rates be displayed (given in AssetRequests) - /// ; 0 to disable - /// HitRateDisplay = 100 - /// - /// ; Set to false for disk cache only. - /// MemoryCacheEnabled = true - /// - /// ; How long {in hours} to keep assets cached in memory, .5 == 30 minutes - /// MemoryCacheTimeout = 2 - /// - /// ; How long {in hours} to keep assets cached on disk, .5 == 30 minutes - /// ; Specify 0 if you do not want your disk cache to expire - /// FileCacheTimeout = 0 - /// - /// ; How often {in hours} should the disk be checked for expired filed - /// ; Specify 0 to disable expiration checking - /// FileCleanupTimer = .166 ;roughly every 10 minutes - /// - /// ; If WAIT_ON_INPROGRESS_REQUESTS has been defined then this specifies how - /// ; long (in miliseconds) to block a request thread while trying to complete - /// ; writing to disk. - /// WaitOnInprogressTimeout = 3000 - /// ------- - /// - - [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] - public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache - { - private static readonly ILog m_log = - LogManager.GetLogger( - MethodBase.GetCurrentMethod().DeclaringType); - - private bool m_Enabled = false; - - private const string m_ModuleName = "FlotsamAssetCache"; - private const string m_DefaultCacheDirectory = m_ModuleName; - private string m_CacheDirectory = m_DefaultCacheDirectory; - - - private List m_InvalidChars = new List(); - - private int m_LogLevel = 1; - private ulong m_HitRateDisplay = 1; // How often to display hit statistics, given in requests - - private static ulong m_Requests = 0; - private static ulong m_RequestsForInprogress = 0; - private static ulong m_DiskHits = 0; - private static ulong m_MemoryHits = 0; - private static double m_HitRateMemory = 0.0; - private static double m_HitRateFile = 0.0; - -#if WAIT_ON_INPROGRESS_REQUESTS - private Dictionary m_CurrentlyWriting = new Dictionary(); - private int m_WaitOnInprogressTimeout = 3000; -#else - private List m_CurrentlyWriting = new List(); -#endif - - private ExpiringCache m_MemoryCache = new ExpiringCache(); - private bool m_MemoryCacheEnabled = true; - - // Expiration is expressed in hours. - private const double m_DefaultMemoryExpiration = 1.0; - private const double m_DefaultFileExpiration = 48; - private TimeSpan m_MemoryExpiration = TimeSpan.Zero; - private TimeSpan m_FileExpiration = TimeSpan.Zero; - private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.Zero; - - private System.Timers.Timer m_CachCleanTimer = new System.Timers.Timer(); - - public FlotsamAssetCache() - { - m_InvalidChars.AddRange(Path.GetInvalidPathChars()); - m_InvalidChars.AddRange(Path.GetInvalidFileNameChars()); - } - - public string Name - { - get { return m_ModuleName; } - } - - public void Initialise(IConfigSource source) - { - IConfig moduleConfig = source.Configs["Modules"]; - - if (moduleConfig != null) - { - string name = moduleConfig.GetString("AssetCaching", this.Name); - - if (name == Name) - { - IConfig assetConfig = source.Configs["AssetCache"]; - if (assetConfig == null) - { - m_log.Error("[ASSET CACHE]: AssetCache missing from OpenSim.ini"); - return; - } - - m_Enabled = true; - - m_log.InfoFormat("[ASSET CACHE]: {0} enabled", this.Name); - - m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); - m_log.InfoFormat("[ASSET CACHE]: Cache Directory", m_DefaultCacheDirectory); - - m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", true); - m_MemoryExpiration = TimeSpan.FromHours(assetConfig.GetDouble("MemoryCacheTimeout", m_DefaultMemoryExpiration)); - -#if WAIT_ON_INPROGRESS_REQUESTS - m_WaitOnInprogressTimeout = assetConfig.GetInt("WaitOnInprogressTimeout", 3000); -#endif - - m_HitRateDisplay = (ulong)assetConfig.GetInt("HitRateDisplay", 1); - - m_FileExpiration = TimeSpan.FromHours(assetConfig.GetDouble("FileCacheTimeout", m_DefaultFileExpiration)); - m_FileExpirationCleanupTimer = TimeSpan.FromHours(assetConfig.GetDouble("FileCleanupTimer", m_DefaultFileExpiration)); - if ((m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) - { - m_CachCleanTimer.Interval = m_FileExpirationCleanupTimer.TotalMilliseconds; - m_CachCleanTimer.AutoReset = true; - m_CachCleanTimer.Elapsed += CleanupExpiredFiles; - m_CachCleanTimer.Enabled = true; - m_CachCleanTimer.Start(); - } - else - { - m_CachCleanTimer.Enabled = false; - } - } - } - } - - public void PostInitialise() - { - } - - public void Close() - { - } - - public void AddRegion(Scene scene) - { - if (m_Enabled) - scene.RegisterModuleInterface(this); - } - - public void RemoveRegion(Scene scene) - { - } - - public void RegionLoaded(Scene scene) - { - } - - //////////////////////////////////////////////////////////// - // IImprovedAssetCache - // - - private void UpdateMemoryCache(string key, AssetBase asset) - { - if( m_MemoryCacheEnabled ) - { - if (m_MemoryExpiration > TimeSpan.Zero) - { - m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); - } - else - { - m_MemoryCache.AddOrUpdate(key, asset, DateTime.MaxValue); - } - } - } - - public void Cache(AssetBase asset) - { - // TODO: Spawn this off to some seperate thread to do the actual writing - if (asset != null) - { - UpdateMemoryCache(asset.ID, asset); - - string filename = GetFileName(asset.ID); - - try - { - // If the file is already cached, don't cache it, just touch it so access time is updated - if (File.Exists(filename)) - { - File.SetLastAccessTime(filename, DateTime.Now); - } else { - - // Once we start writing, make sure we flag that we're writing - // that object to the cache so that we don't try to write the - // same file multiple times. - lock (m_CurrentlyWriting) - { -#if WAIT_ON_INPROGRESS_REQUESTS - if (m_CurrentlyWriting.ContainsKey(filename)) - { - return; - } - else - { - m_CurrentlyWriting.Add(filename, new ManualResetEvent(false)); - } - -#else - if (m_CurrentlyWriting.Contains(filename)) - { - return; - } - else - { - m_CurrentlyWriting.Add(filename); - } -#endif - - } - - ThreadPool.QueueUserWorkItem( - delegate - { - WriteFileCache(filename, asset); - } - ); - } - } - catch (Exception e) - { - LogException(e); - } - } - } - - public AssetBase Get(string id) - { - m_Requests++; - - AssetBase asset = null; - - if (m_MemoryCacheEnabled && m_MemoryCache.TryGetValue(id, out asset)) - { - m_MemoryHits++; - } - else - { - string filename = GetFileName(id); - if (File.Exists(filename)) - { - try - { - FileStream stream = File.Open(filename, FileMode.Open); - BinaryFormatter bformatter = new BinaryFormatter(); - - asset = (AssetBase)bformatter.Deserialize(stream); - stream.Close(); - - UpdateMemoryCache(id, asset); - - m_DiskHits++; - } - catch (System.Runtime.Serialization.SerializationException e) - { - LogException(e); - - // If there was a problem deserializing the asset, the asset may - // either be corrupted OR was serialized under an old format - // {different version of AssetBase} -- we should attempt to - // delete it and re-cache - File.Delete(filename); - } - catch (Exception e) - { - LogException(e); - } - } - - -#if WAIT_ON_INPROGRESS_REQUESTS - // Check if we're already downloading this asset. If so, try to wait for it to - // download. - if (m_WaitOnInprogressTimeout > 0) - { - m_RequestsForInprogress++; - - ManualResetEvent waitEvent; - if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) - { - waitEvent.WaitOne(m_WaitOnInprogressTimeout); - return Get(id); - } - } -#else - // Track how often we have the problem that an asset is requested while - // it is still being downloaded by a previous request. - if (m_CurrentlyWriting.Contains(filename)) - { - m_RequestsForInprogress++; - } -#endif - } - - if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) - { - m_HitRateFile = (double)m_DiskHits / m_Requests * 100.0; - - m_log.InfoFormat("[ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); - m_log.InfoFormat("[ASSET CACHE]: File Hit Rate {0}% for {1} requests", m_HitRateFile.ToString("0.00"), m_Requests); - - if (m_MemoryCacheEnabled) - { - m_HitRateMemory = (double)m_MemoryHits / m_Requests * 100.0; - m_log.InfoFormat("[ASSET CACHE]: Memory Hit Rate {0}% for {1} requests", m_HitRateMemory.ToString("0.00"), m_Requests); - } - - m_log.InfoFormat("[ASSET CACHE]: {0} unnessesary requests due to requests for assets that are currently downloading.", m_RequestsForInprogress); - - } - - return asset; - } - - public void Expire(string id) - { - if (m_LogLevel >= 2) - m_log.DebugFormat("[ASSET CACHE]: Expiring Asset {0}.", id); - - try - { - string filename = GetFileName(id); - if (File.Exists(filename)) - { - File.Delete(filename); - } - - if( m_MemoryCacheEnabled ) - m_MemoryCache.Remove(id); - } - catch (Exception e) - { - LogException(e); - } - } - - public void Clear() - { - if (m_LogLevel >= 2) - m_log.Debug("[ASSET CACHE]: Clearing Cache."); - - foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) - { - Directory.Delete(dir); - } - - if( m_MemoryCacheEnabled ) - m_MemoryCache.Clear(); - } - - private void CleanupExpiredFiles(object source, ElapsedEventArgs e) - { - if (m_LogLevel >= 2) - m_log.DebugFormat("[ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration.ToString()); - - foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) - { - foreach (string file in Directory.GetFiles(dir)) - { - if (DateTime.Now - File.GetLastAccessTime(file) > m_FileExpiration) - { - File.Delete(file); - } - } - } - } - - private string GetFileName(string id) - { - // Would it be faster to just hash the darn thing? - foreach (char c in m_InvalidChars) - { - id = id.Replace(c, '_'); - } - - string p = id.Substring(id.Length - 4); - p = Path.Combine(p, id); - return Path.Combine(m_CacheDirectory, p); - } - - private void WriteFileCache(string filename, AssetBase asset) - { - try - { - // Make sure the target cache directory exists - string directory = Path.GetDirectoryName(filename); - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } - - // Write file first to a temp name, so that it doesn't look - // like it's already cached while it's still writing. - string tempname = Path.Combine(directory, Path.GetRandomFileName()); - Stream stream = File.Open(tempname, FileMode.Create); - BinaryFormatter bformatter = new BinaryFormatter(); - bformatter.Serialize(stream, asset); - stream.Close(); - - // Now that it's written, rename it so that it can be found. - File.Move(tempname, filename); - - if (m_LogLevel >= 2) - m_log.DebugFormat("[ASSET CACHE]: Cache Stored :: {0}", asset.ID); - } - catch (Exception e) - { - LogException(e); - } - finally - { - // Even if the write fails with an exception, we need to make sure - // that we release the lock on that file, otherwise it'll never get - // cached - lock (m_CurrentlyWriting) - { -#if WAIT_ON_INPROGRESS_REQUESTS - ManualResetEvent waitEvent; - if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) - { - m_CurrentlyWriting.Remove(filename); - waitEvent.Set(); - } -#else - if (m_CurrentlyWriting.Contains(filename)) - { - m_CurrentlyWriting.Remove(filename); - } -#endif - } - - } - } - - private static void LogException(Exception e) - { - string[] text = e.ToString().Split(new char[] { '\n' }); - foreach (string t in text) - { - m_log.ErrorFormat("[ASSET CACHE]: {0} ", t); - } - } - } -} +/* +Copyright (c) Contributors, http://osflotsam.org/ +See CONTRIBUTORS.TXT for a full list of copyright holders. + +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 Flotsam Project 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 DEVELOPERS ``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 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. */ + +// Uncomment to make asset Get requests for existing +// #define WAIT_ON_INPROGRESS_REQUESTS + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Threading; +using System.Timers; + +using log4net; +using Nini.Config; +using Mono.Addins; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + + +[assembly: Addin("FlotsamAssetCache", "1.1")] +[assembly: AddinDependency("OpenSim", "0.5")] + +namespace Flotsam.RegionModules.AssetCache +{ + /// + /// OpenSim.ini Options: + /// ------- + /// [Modules] + /// AssetCaching = "FlotsamAssetCache" + /// + /// [AssetCache] + /// ; cache directory can be shared by multiple instances + /// CacheDirectory = /directory/writable/by/OpenSim/instance + /// + /// ; Log level + /// ; 0 - (Error) Errors only + /// ; 1 - (Info) Hit Rate Stats + Level 0 + /// ; 2 - (Debug) Cache Activity (Reads/Writes) + Level 1 + /// ; + /// LogLevel = 1 + /// + /// ; How often should hit rates be displayed (given in AssetRequests) + /// ; 0 to disable + /// HitRateDisplay = 100 + /// + /// ; Set to false for disk cache only. + /// MemoryCacheEnabled = true + /// + /// ; How long {in hours} to keep assets cached in memory, .5 == 30 minutes + /// MemoryCacheTimeout = 2 + /// + /// ; How long {in hours} to keep assets cached on disk, .5 == 30 minutes + /// ; Specify 0 if you do not want your disk cache to expire + /// FileCacheTimeout = 0 + /// + /// ; How often {in hours} should the disk be checked for expired filed + /// ; Specify 0 to disable expiration checking + /// FileCleanupTimer = .166 ;roughly every 10 minutes + /// + /// ; If WAIT_ON_INPROGRESS_REQUESTS has been defined then this specifies how + /// ; long (in miliseconds) to block a request thread while trying to complete + /// ; writing to disk. + /// WaitOnInprogressTimeout = 3000 + /// ------- + /// + + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + + private const string m_ModuleName = "FlotsamAssetCache"; + private const string m_DefaultCacheDirectory = m_ModuleName; + private string m_CacheDirectory = m_DefaultCacheDirectory; + + + private List m_InvalidChars = new List(); + + private int m_LogLevel = 1; + private ulong m_HitRateDisplay = 1; // How often to display hit statistics, given in requests + + private static ulong m_Requests = 0; + private static ulong m_RequestsForInprogress = 0; + private static ulong m_DiskHits = 0; + private static ulong m_MemoryHits = 0; + private static double m_HitRateMemory = 0.0; + private static double m_HitRateFile = 0.0; + +#if WAIT_ON_INPROGRESS_REQUESTS + private Dictionary m_CurrentlyWriting = new Dictionary(); + private int m_WaitOnInprogressTimeout = 3000; +#else + private List m_CurrentlyWriting = new List(); +#endif + + private ExpiringCache m_MemoryCache = new ExpiringCache(); + private bool m_MemoryCacheEnabled = true; + + // Expiration is expressed in hours. + private const double m_DefaultMemoryExpiration = 1.0; + private const double m_DefaultFileExpiration = 48; + private TimeSpan m_MemoryExpiration = TimeSpan.Zero; + private TimeSpan m_FileExpiration = TimeSpan.Zero; + private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.Zero; + + private System.Timers.Timer m_CachCleanTimer = new System.Timers.Timer(); + + public FlotsamAssetCache() + { + m_InvalidChars.AddRange(Path.GetInvalidPathChars()); + m_InvalidChars.AddRange(Path.GetInvalidFileNameChars()); + } + + public string Name + { + get { return m_ModuleName; } + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + + if (moduleConfig != null) + { + string name = moduleConfig.GetString("AssetCaching", this.Name); + + if (name == Name) + { + IConfig assetConfig = source.Configs["AssetCache"]; + if (assetConfig == null) + { + m_log.Error("[ASSET CACHE]: AssetCache missing from OpenSim.ini"); + return; + } + + m_Enabled = true; + + m_log.InfoFormat("[ASSET CACHE]: {0} enabled", this.Name); + + m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); + m_log.InfoFormat("[ASSET CACHE]: Cache Directory", m_DefaultCacheDirectory); + + m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", true); + m_MemoryExpiration = TimeSpan.FromHours(assetConfig.GetDouble("MemoryCacheTimeout", m_DefaultMemoryExpiration)); + +#if WAIT_ON_INPROGRESS_REQUESTS + m_WaitOnInprogressTimeout = assetConfig.GetInt("WaitOnInprogressTimeout", 3000); +#endif + + m_HitRateDisplay = (ulong)assetConfig.GetInt("HitRateDisplay", 1); + + m_FileExpiration = TimeSpan.FromHours(assetConfig.GetDouble("FileCacheTimeout", m_DefaultFileExpiration)); + m_FileExpirationCleanupTimer = TimeSpan.FromHours(assetConfig.GetDouble("FileCleanupTimer", m_DefaultFileExpiration)); + if ((m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) + { + m_CachCleanTimer.Interval = m_FileExpirationCleanupTimer.TotalMilliseconds; + m_CachCleanTimer.AutoReset = true; + m_CachCleanTimer.Elapsed += CleanupExpiredFiles; + m_CachCleanTimer.Enabled = true; + m_CachCleanTimer.Start(); + } + else + { + m_CachCleanTimer.Enabled = false; + } + } + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + if (m_Enabled) + scene.RegisterModuleInterface(this); + } + + public void RemoveRegion(Scene scene) + { + } + + public void RegionLoaded(Scene scene) + { + } + + //////////////////////////////////////////////////////////// + // IImprovedAssetCache + // + + private void UpdateMemoryCache(string key, AssetBase asset) + { + if( m_MemoryCacheEnabled ) + { + if (m_MemoryExpiration > TimeSpan.Zero) + { + m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); + } + else + { + m_MemoryCache.AddOrUpdate(key, asset, DateTime.MaxValue); + } + } + } + + public void Cache(AssetBase asset) + { + // TODO: Spawn this off to some seperate thread to do the actual writing + if (asset != null) + { + UpdateMemoryCache(asset.ID, asset); + + string filename = GetFileName(asset.ID); + + try + { + // If the file is already cached, don't cache it, just touch it so access time is updated + if (File.Exists(filename)) + { + File.SetLastAccessTime(filename, DateTime.Now); + } else { + + // Once we start writing, make sure we flag that we're writing + // that object to the cache so that we don't try to write the + // same file multiple times. + lock (m_CurrentlyWriting) + { +#if WAIT_ON_INPROGRESS_REQUESTS + if (m_CurrentlyWriting.ContainsKey(filename)) + { + return; + } + else + { + m_CurrentlyWriting.Add(filename, new ManualResetEvent(false)); + } + +#else + if (m_CurrentlyWriting.Contains(filename)) + { + return; + } + else + { + m_CurrentlyWriting.Add(filename); + } +#endif + + } + + ThreadPool.QueueUserWorkItem( + delegate + { + WriteFileCache(filename, asset); + } + ); + } + } + catch (Exception e) + { + LogException(e); + } + } + } + + public AssetBase Get(string id) + { + m_Requests++; + + AssetBase asset = null; + + if (m_MemoryCacheEnabled && m_MemoryCache.TryGetValue(id, out asset)) + { + m_MemoryHits++; + } + else + { + string filename = GetFileName(id); + if (File.Exists(filename)) + { + try + { + FileStream stream = File.Open(filename, FileMode.Open); + BinaryFormatter bformatter = new BinaryFormatter(); + + asset = (AssetBase)bformatter.Deserialize(stream); + stream.Close(); + + UpdateMemoryCache(id, asset); + + m_DiskHits++; + } + catch (System.Runtime.Serialization.SerializationException e) + { + LogException(e); + + // If there was a problem deserializing the asset, the asset may + // either be corrupted OR was serialized under an old format + // {different version of AssetBase} -- we should attempt to + // delete it and re-cache + File.Delete(filename); + } + catch (Exception e) + { + LogException(e); + } + } + + +#if WAIT_ON_INPROGRESS_REQUESTS + // Check if we're already downloading this asset. If so, try to wait for it to + // download. + if (m_WaitOnInprogressTimeout > 0) + { + m_RequestsForInprogress++; + + ManualResetEvent waitEvent; + if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) + { + waitEvent.WaitOne(m_WaitOnInprogressTimeout); + return Get(id); + } + } +#else + // Track how often we have the problem that an asset is requested while + // it is still being downloaded by a previous request. + if (m_CurrentlyWriting.Contains(filename)) + { + m_RequestsForInprogress++; + } +#endif + } + + if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) + { + m_HitRateFile = (double)m_DiskHits / m_Requests * 100.0; + + m_log.InfoFormat("[ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); + m_log.InfoFormat("[ASSET CACHE]: File Hit Rate {0}% for {1} requests", m_HitRateFile.ToString("0.00"), m_Requests); + + if (m_MemoryCacheEnabled) + { + m_HitRateMemory = (double)m_MemoryHits / m_Requests * 100.0; + m_log.InfoFormat("[ASSET CACHE]: Memory Hit Rate {0}% for {1} requests", m_HitRateMemory.ToString("0.00"), m_Requests); + } + + m_log.InfoFormat("[ASSET CACHE]: {0} unnessesary requests due to requests for assets that are currently downloading.", m_RequestsForInprogress); + + } + + return asset; + } + + public void Expire(string id) + { + if (m_LogLevel >= 2) + m_log.DebugFormat("[ASSET CACHE]: Expiring Asset {0}.", id); + + try + { + string filename = GetFileName(id); + if (File.Exists(filename)) + { + File.Delete(filename); + } + + if( m_MemoryCacheEnabled ) + m_MemoryCache.Remove(id); + } + catch (Exception e) + { + LogException(e); + } + } + + public void Clear() + { + if (m_LogLevel >= 2) + m_log.Debug("[ASSET CACHE]: Clearing Cache."); + + foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) + { + Directory.Delete(dir); + } + + if( m_MemoryCacheEnabled ) + m_MemoryCache.Clear(); + } + + private void CleanupExpiredFiles(object source, ElapsedEventArgs e) + { + if (m_LogLevel >= 2) + m_log.DebugFormat("[ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration.ToString()); + + foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) + { + foreach (string file in Directory.GetFiles(dir)) + { + if (DateTime.Now - File.GetLastAccessTime(file) > m_FileExpiration) + { + File.Delete(file); + } + } + } + } + + private string GetFileName(string id) + { + // Would it be faster to just hash the darn thing? + foreach (char c in m_InvalidChars) + { + id = id.Replace(c, '_'); + } + + string p = id.Substring(id.Length - 4); + p = Path.Combine(p, id); + return Path.Combine(m_CacheDirectory, p); + } + + private void WriteFileCache(string filename, AssetBase asset) + { + try + { + // Make sure the target cache directory exists + string directory = Path.GetDirectoryName(filename); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + // Write file first to a temp name, so that it doesn't look + // like it's already cached while it's still writing. + string tempname = Path.Combine(directory, Path.GetRandomFileName()); + Stream stream = File.Open(tempname, FileMode.Create); + BinaryFormatter bformatter = new BinaryFormatter(); + bformatter.Serialize(stream, asset); + stream.Close(); + + // Now that it's written, rename it so that it can be found. + File.Move(tempname, filename); + + if (m_LogLevel >= 2) + m_log.DebugFormat("[ASSET CACHE]: Cache Stored :: {0}", asset.ID); + } + catch (Exception e) + { + LogException(e); + } + finally + { + // Even if the write fails with an exception, we need to make sure + // that we release the lock on that file, otherwise it'll never get + // cached + lock (m_CurrentlyWriting) + { +#if WAIT_ON_INPROGRESS_REQUESTS + ManualResetEvent waitEvent; + if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) + { + m_CurrentlyWriting.Remove(filename); + waitEvent.Set(); + } +#else + if (m_CurrentlyWriting.Contains(filename)) + { + m_CurrentlyWriting.Remove(filename); + } +#endif + } + + } + } + + private static void LogException(Exception e) + { + string[] text = e.ToString().Split(new char[] { '\n' }); + foreach (string t in text) + { + m_log.ErrorFormat("[ASSET CACHE]: {0} ", t); + } + } + } +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectors/Inventory/LocalInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectors/Inventory/LocalInventoryServiceConnector.cs index c89f29556c..6b72e9bc18 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectors/Inventory/LocalInventoryServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectors/Inventory/LocalInventoryServiceConnector.cs @@ -1,273 +1,273 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * 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 OpenSimulator Project 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 DEVELOPERS ``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 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 log4net; -using Nini.Config; -using System; -using System.Collections.Generic; -using System.Reflection; -using OpenSim.Framework; -using OpenSim.Data; -using OpenSim.Server.Base; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; -using OpenSim.Services.Interfaces; -using OpenMetaverse; - -namespace OpenSim.Region.CoreModules.ServiceConnectors.Inventory -{ - public class LocalInventoryServicesConnector : - ISharedRegionModule, IInventoryService - { - private static readonly ILog m_log = - LogManager.GetLogger( - MethodBase.GetCurrentMethod().DeclaringType); - - private IInventoryService m_InventoryService; - - private bool m_Enabled = false; - - public string Name - { - get { return "LocalInventoryServicesConnector"; } - } - - public void Initialise(IConfigSource source) - { - IConfig moduleConfig = source.Configs["Modules"]; - if (moduleConfig != null) - { - string name = moduleConfig.GetString("InventoryServices", ""); - if (name == Name) - { - IConfig assetConfig = source.Configs["InventoryService"]; - if (assetConfig == null) - { - m_log.Error("[INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); - return; - } - - string serviceDll = assetConfig.GetString("LocalServiceModule", - String.Empty); - - if (serviceDll == String.Empty) - { - m_log.Error("[INVENTORY CONNECTOR]: No LocalServiceModule named in section InventoryService"); - return; - } - - Object[] args = new Object[] { source }; - m_InventoryService = - ServerUtils.LoadPlugin(serviceDll, - args); - - if (m_InventoryService == null) - { - m_log.Error("[INVENTORY CONNECTOR]: Can't load asset service"); - return; - } - - //List plugins - // = DataPluginFactory.LoadDataPlugins( - // configSettings.StandaloneInventoryPlugin, - // configSettings.StandaloneInventorySource); - - //foreach (IInventoryDataPlugin plugin in plugins) - //{ - // // Using the OSP wrapper plugin for database plugins should be made configurable at some point - // m_InventoryService.AddPlugin(new OspInventoryWrapperPlugin(plugin, this)); - //} - - m_Enabled = true; - m_log.Info("[INVENTORY CONNECTOR]: Local asset connector enabled"); - } - } - } - - public void PostInitialise() - { - } - - public void Close() - { - } - - public void AddRegion(Scene scene) - { - if (!m_Enabled) - return; - - scene.RegisterModuleInterface(this); - } - - public void RemoveRegion(Scene scene) - { - } - - public void RegionLoaded(Scene scene) - { - if (!m_Enabled) - return; - - m_log.InfoFormat("[INVENTORY CONNECTOR]: Enabled local assets for region {0}", scene.RegionInfo.RegionName); - - } - - #region IInventoryService - - public bool CreateUserInventory(UUID user) - { - return m_InventoryService.CreateUserInventory(user); - } - - public List GetInventorySkeleton(UUID userId) - { - return m_InventoryService.GetInventorySkeleton(userId); - } - - public InventoryCollection GetUserInventory(UUID id) - { - return m_InventoryService.GetUserInventory(id); - } - - public void GetUserInventory(UUID userID, InventoryReceiptCallback callback) - { - m_InventoryService.GetUserInventory(userID, callback); - } - - public List GetFolderItems(UUID folderID) - { - return m_InventoryService.GetFolderItems(folderID); - } - - /// - /// Add a new folder to the user's inventory - /// - /// - /// true if the folder was successfully added - public bool AddFolder(InventoryFolderBase folder) - { - return m_InventoryService.AddFolder(folder); - } - - /// - /// Update a folder in the user's inventory - /// - /// - /// true if the folder was successfully updated - public bool UpdateFolder(InventoryFolderBase folder) - { - return m_InventoryService.UpdateFolder(folder); - } - - /// - /// Move an inventory folder to a new location - /// - /// A folder containing the details of the new location - /// true if the folder was successfully moved - public bool MoveFolder(InventoryFolderBase folder) - { - return m_InventoryService.MoveFolder(folder); - } - - /// - /// Purge an inventory folder of all its items and subfolders. - /// - /// - /// true if the folder was successfully purged - public bool PurgeFolder(InventoryFolderBase folder) - { - return m_InventoryService.PurgeFolder(folder); - } - - /// - /// Add a new item to the user's inventory - /// - /// - /// true if the item was successfully added - public bool AddItem(InventoryItemBase item) - { - return m_InventoryService.AddItem(item); - } - - /// - /// Update an item in the user's inventory - /// - /// - /// true if the item was successfully updated - public bool UpdateItem(InventoryItemBase item) - { - return m_InventoryService.UpdateItem(item); - } - - /// - /// Delete an item from the user's inventory - /// - /// - /// true if the item was successfully deleted - public bool DeleteItem(InventoryItemBase item) - { - return m_InventoryService.DeleteItem(item); - } - - public InventoryItemBase QueryItem(InventoryItemBase item) - { - return m_InventoryService.QueryItem(item); - } - - public InventoryFolderBase QueryFolder(InventoryFolderBase folder) - { - return m_InventoryService.QueryFolder(folder); - } - - /// - /// Does the given user have an inventory structure? - /// - /// - /// - public bool HasInventoryForUser(UUID userID) - { - return m_InventoryService.HasInventoryForUser(userID); - } - - /// - /// Retrieve the root inventory folder for the given user. - /// - /// - /// null if no root folder was found - public InventoryFolderBase RequestRootFolder(UUID userID) - { - return m_InventoryService.RequestRootFolder(userID); - } - - public List GetActiveGestures(UUID userId) - { - return m_InventoryService.GetActiveGestures(userId); - } - #endregion IInventoryService - } -} +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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 OpenSimulator Project 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 DEVELOPERS ``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 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 log4net; +using Nini.Config; +using System; +using System.Collections.Generic; +using System.Reflection; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Server.Base; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Region.CoreModules.ServiceConnectors.Inventory +{ + public class LocalInventoryServicesConnector : + ISharedRegionModule, IInventoryService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private IInventoryService m_InventoryService; + + private bool m_Enabled = false; + + public string Name + { + get { return "LocalInventoryServicesConnector"; } + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("InventoryServices", ""); + if (name == Name) + { + IConfig assetConfig = source.Configs["InventoryService"]; + if (assetConfig == null) + { + m_log.Error("[INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); + return; + } + + string serviceDll = assetConfig.GetString("LocalServiceModule", + String.Empty); + + if (serviceDll == String.Empty) + { + m_log.Error("[INVENTORY CONNECTOR]: No LocalServiceModule named in section InventoryService"); + return; + } + + Object[] args = new Object[] { source }; + m_InventoryService = + ServerUtils.LoadPlugin(serviceDll, + args); + + if (m_InventoryService == null) + { + m_log.Error("[INVENTORY CONNECTOR]: Can't load asset service"); + return; + } + + //List plugins + // = DataPluginFactory.LoadDataPlugins( + // configSettings.StandaloneInventoryPlugin, + // configSettings.StandaloneInventorySource); + + //foreach (IInventoryDataPlugin plugin in plugins) + //{ + // // Using the OSP wrapper plugin for database plugins should be made configurable at some point + // m_InventoryService.AddPlugin(new OspInventoryWrapperPlugin(plugin, this)); + //} + + m_Enabled = true; + m_log.Info("[INVENTORY CONNECTOR]: Local asset connector enabled"); + } + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.RegisterModuleInterface(this); + } + + public void RemoveRegion(Scene scene) + { + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + m_log.InfoFormat("[INVENTORY CONNECTOR]: Enabled local assets for region {0}", scene.RegionInfo.RegionName); + + } + + #region IInventoryService + + public bool CreateUserInventory(UUID user) + { + return m_InventoryService.CreateUserInventory(user); + } + + public List GetInventorySkeleton(UUID userId) + { + return m_InventoryService.GetInventorySkeleton(userId); + } + + public InventoryCollection GetUserInventory(UUID id) + { + return m_InventoryService.GetUserInventory(id); + } + + public void GetUserInventory(UUID userID, InventoryReceiptCallback callback) + { + m_InventoryService.GetUserInventory(userID, callback); + } + + public List GetFolderItems(UUID folderID) + { + return m_InventoryService.GetFolderItems(folderID); + } + + /// + /// Add a new folder to the user's inventory + /// + /// + /// true if the folder was successfully added + public bool AddFolder(InventoryFolderBase folder) + { + return m_InventoryService.AddFolder(folder); + } + + /// + /// Update a folder in the user's inventory + /// + /// + /// true if the folder was successfully updated + public bool UpdateFolder(InventoryFolderBase folder) + { + return m_InventoryService.UpdateFolder(folder); + } + + /// + /// Move an inventory folder to a new location + /// + /// A folder containing the details of the new location + /// true if the folder was successfully moved + public bool MoveFolder(InventoryFolderBase folder) + { + return m_InventoryService.MoveFolder(folder); + } + + /// + /// Purge an inventory folder of all its items and subfolders. + /// + /// + /// true if the folder was successfully purged + public bool PurgeFolder(InventoryFolderBase folder) + { + return m_InventoryService.PurgeFolder(folder); + } + + /// + /// Add a new item to the user's inventory + /// + /// + /// true if the item was successfully added + public bool AddItem(InventoryItemBase item) + { + return m_InventoryService.AddItem(item); + } + + /// + /// Update an item in the user's inventory + /// + /// + /// true if the item was successfully updated + public bool UpdateItem(InventoryItemBase item) + { + return m_InventoryService.UpdateItem(item); + } + + /// + /// Delete an item from the user's inventory + /// + /// + /// true if the item was successfully deleted + public bool DeleteItem(InventoryItemBase item) + { + return m_InventoryService.DeleteItem(item); + } + + public InventoryItemBase QueryItem(InventoryItemBase item) + { + return m_InventoryService.QueryItem(item); + } + + public InventoryFolderBase QueryFolder(InventoryFolderBase folder) + { + return m_InventoryService.QueryFolder(folder); + } + + /// + /// Does the given user have an inventory structure? + /// + /// + /// + public bool HasInventoryForUser(UUID userID) + { + return m_InventoryService.HasInventoryForUser(userID); + } + + /// + /// Retrieve the root inventory folder for the given user. + /// + /// + /// null if no root folder was found + public InventoryFolderBase RequestRootFolder(UUID userID) + { + return m_InventoryService.RequestRootFolder(userID); + } + + public List GetActiveGestures(UUID userId) + { + return m_InventoryService.GetActiveGestures(userId); + } + #endregion IInventoryService + } +} diff --git a/OpenSim/Server/Handlers/Inventory/InventoryServerInConnector.cs b/OpenSim/Server/Handlers/Inventory/InventoryServerInConnector.cs index c8d08de462..3e3064a9fc 100644 --- a/OpenSim/Server/Handlers/Inventory/InventoryServerInConnector.cs +++ b/OpenSim/Server/Handlers/Inventory/InventoryServerInConnector.cs @@ -1,276 +1,276 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * 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 OpenSimulator Project 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 DEVELOPERS ``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 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; -using System.Collections.Generic; -using System.Net; -using System.Reflection; -using log4net; -using Nini.Config; -using Nwc.XmlRpc; -using OpenSim.Server.Base; -using OpenSim.Services.Interfaces; -using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Server.Handlers.Base; -using OpenMetaverse; - -namespace OpenSim.Server.Handlers.Inventory -{ - public class InventoryServiceInConnector : ServiceConnector - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private IInventoryService m_InventoryService; - - private bool m_doLookup = false; - - //private static readonly int INVENTORY_DEFAULT_SESSION_TIME = 30; // secs - //private AuthedSessionCache m_session_cache = new AuthedSessionCache(INVENTORY_DEFAULT_SESSION_TIME); - - private string m_userserver_url; - - public InventoryServiceInConnector(IConfigSource config, IHttpServer server) : - base(config, server) - { - IConfig serverConfig = config.Configs["InventoryService"]; - if (serverConfig == null) - throw new Exception("No section 'InventoryService' in config file"); - - string inventoryService = serverConfig.GetString("LocalServiceModule", - String.Empty); - - if (inventoryService == String.Empty) - throw new Exception("No InventoryService in config file"); - - Object[] args = new Object[] { config }; - m_InventoryService = - ServerUtils.LoadPlugin(inventoryService, args); - - m_userserver_url = serverConfig.GetString("UserServerURI", String.Empty); - m_doLookup = serverConfig.GetBoolean("SessionAuthentication", false); - - AddHttpHandlers(server); - } - - protected virtual void AddHttpHandlers(IHttpServer m_httpServer) - { - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/GetInventory/", GetUserInventory, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/UpdateFolder/", m_InventoryService.UpdateFolder, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/MoveFolder/", m_InventoryService.MoveFolder, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/PurgeFolder/", m_InventoryService.PurgeFolder, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/DeleteItem/", m_InventoryService.DeleteItem, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/QueryItem/", m_InventoryService.QueryItem, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/QueryFolder/", m_InventoryService.QueryFolder, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseTrustedHandler( - "POST", "/CreateInventory/", CreateUsersInventory, CheckTrustSource)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/NewFolder/", m_InventoryService.AddFolder, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseTrustedHandler( - "POST", "/CreateFolder/", m_InventoryService.AddFolder, CheckTrustSource)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseSecureHandler( - "POST", "/NewItem/", m_InventoryService.AddItem, CheckAuthSession)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseTrustedHandler( - "POST", "/AddNewItem/", m_InventoryService.AddItem, CheckTrustSource)); - - m_httpServer.AddStreamHandler( - new RestDeserialiseTrustedHandler>( - "POST", "/GetItems/", GetFolderItems, CheckTrustSource)); - - // for persistent active gestures - m_httpServer.AddStreamHandler( - new RestDeserialiseTrustedHandler> - ("POST", "/ActiveGestures/", GetActiveGestures, CheckTrustSource)); - - // WARNING: Root folders no longer just delivers the root and immediate child folders (e.g - // system folders such as Objects, Textures), but it now returns the entire inventory skeleton. - // It would have been better to rename this request, but complexities in the BaseHttpServer - // (e.g. any http request not found is automatically treated as an xmlrpc request) make it easier - // to do this for now. - m_httpServer.AddStreamHandler( - new RestDeserialiseTrustedHandler> - ("POST", "/RootFolders/", GetInventorySkeleton, CheckTrustSource)); - } - - #region Wrappers for converting the Guid parameter - - public InventoryCollection GetUserInventory(Guid guid) - { - UUID userID = new UUID(guid); - return m_InventoryService.GetUserInventory(userID); - } - - public List GetFolderItems(Guid folderID) - { - List allItems = new List(); - - List items = m_InventoryService.GetFolderItems(new UUID(folderID)); - - if (items != null) - { - allItems.InsertRange(0, items); - } - return allItems; - } - - public bool CreateUsersInventory(Guid rawUserID) - { - UUID userID = new UUID(rawUserID); - - - return m_InventoryService.CreateUserInventory(userID); - } - - public List GetActiveGestures(Guid rawUserID) - { - UUID userID = new UUID(rawUserID); - - return m_InventoryService.GetActiveGestures(userID); - } - - public List GetInventorySkeleton(Guid rawUserID) - { - UUID userID = new UUID(rawUserID); - return m_InventoryService.GetInventorySkeleton(userID); - } - - #endregion - - /// - /// Check that the source of an inventory request is one that we trust. - /// - /// - /// - public bool CheckTrustSource(IPEndPoint peer) - { - if (m_doLookup) - { - m_log.InfoFormat("[INVENTORY IN CONNECTOR]: Checking trusted source {0}", peer); - UriBuilder ub = new UriBuilder(m_userserver_url); - IPAddress[] uaddrs = Dns.GetHostAddresses(ub.Host); - foreach (IPAddress uaddr in uaddrs) - { - if (uaddr.Equals(peer.Address)) - { - return true; - } - } - - m_log.WarnFormat( - "[INVENTORY IN CONNECTOR]: Rejecting request since source {0} was not in the list of trusted sources", - peer); - - return false; - } - else - { - return true; - } - } - - /// - /// Check that the source of an inventory request for a particular agent is a current session belonging to - /// that agent. - /// - /// - /// - /// - public bool CheckAuthSession(string session_id, string avatar_id) - { - if (m_doLookup) - { - m_log.InfoFormat("[INVENTORY IN CONNECTOR]: checking authed session {0} {1}", session_id, avatar_id); - - //if (m_session_cache.getCachedSession(session_id, avatar_id) == null) - //{ - // cache miss, ask userserver - Hashtable requestData = new Hashtable(); - requestData["avatar_uuid"] = avatar_id; - requestData["session_id"] = session_id; - ArrayList SendParams = new ArrayList(); - SendParams.Add(requestData); - XmlRpcRequest UserReq = new XmlRpcRequest("check_auth_session", SendParams); - XmlRpcResponse UserResp = UserReq.Send(m_userserver_url, 3000); - - Hashtable responseData = (Hashtable)UserResp.Value; - if (responseData.ContainsKey("auth_session") && responseData["auth_session"].ToString() == "TRUE") - { - m_log.Info("[INVENTORY IN CONNECTOR]: got authed session from userserver"); - //// add to cache; the session time will be automatically renewed - //m_session_cache.Add(session_id, avatar_id); - return true; - } - //} - //else - //{ - // // cache hits - // m_log.Info("[GRID AGENT INVENTORY]: got authed session from cache"); - // return true; - //} - - m_log.Warn("[INVENTORY IN CONNECTOR]: unknown session_id, request rejected"); - return false; - } - else - { - return true; - } - } - - } -} +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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 OpenSimulator Project 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 DEVELOPERS ``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 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; +using System.Collections.Generic; +using System.Net; +using System.Reflection; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Inventory +{ + public class InventoryServiceInConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IInventoryService m_InventoryService; + + private bool m_doLookup = false; + + //private static readonly int INVENTORY_DEFAULT_SESSION_TIME = 30; // secs + //private AuthedSessionCache m_session_cache = new AuthedSessionCache(INVENTORY_DEFAULT_SESSION_TIME); + + private string m_userserver_url; + + public InventoryServiceInConnector(IConfigSource config, IHttpServer server) : + base(config, server) + { + IConfig serverConfig = config.Configs["InventoryService"]; + if (serverConfig == null) + throw new Exception("No section 'InventoryService' in config file"); + + string inventoryService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (inventoryService == String.Empty) + throw new Exception("No InventoryService in config file"); + + Object[] args = new Object[] { config }; + m_InventoryService = + ServerUtils.LoadPlugin(inventoryService, args); + + m_userserver_url = serverConfig.GetString("UserServerURI", String.Empty); + m_doLookup = serverConfig.GetBoolean("SessionAuthentication", false); + + AddHttpHandlers(server); + } + + protected virtual void AddHttpHandlers(IHttpServer m_httpServer) + { + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/GetInventory/", GetUserInventory, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/UpdateFolder/", m_InventoryService.UpdateFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/MoveFolder/", m_InventoryService.MoveFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/PurgeFolder/", m_InventoryService.PurgeFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/DeleteItem/", m_InventoryService.DeleteItem, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/QueryItem/", m_InventoryService.QueryItem, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/QueryFolder/", m_InventoryService.QueryFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler( + "POST", "/CreateInventory/", CreateUsersInventory, CheckTrustSource)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/NewFolder/", m_InventoryService.AddFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler( + "POST", "/CreateFolder/", m_InventoryService.AddFolder, CheckTrustSource)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/NewItem/", m_InventoryService.AddItem, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler( + "POST", "/AddNewItem/", m_InventoryService.AddItem, CheckTrustSource)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler>( + "POST", "/GetItems/", GetFolderItems, CheckTrustSource)); + + // for persistent active gestures + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler> + ("POST", "/ActiveGestures/", GetActiveGestures, CheckTrustSource)); + + // WARNING: Root folders no longer just delivers the root and immediate child folders (e.g + // system folders such as Objects, Textures), but it now returns the entire inventory skeleton. + // It would have been better to rename this request, but complexities in the BaseHttpServer + // (e.g. any http request not found is automatically treated as an xmlrpc request) make it easier + // to do this for now. + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler> + ("POST", "/RootFolders/", GetInventorySkeleton, CheckTrustSource)); + } + + #region Wrappers for converting the Guid parameter + + public InventoryCollection GetUserInventory(Guid guid) + { + UUID userID = new UUID(guid); + return m_InventoryService.GetUserInventory(userID); + } + + public List GetFolderItems(Guid folderID) + { + List allItems = new List(); + + List items = m_InventoryService.GetFolderItems(new UUID(folderID)); + + if (items != null) + { + allItems.InsertRange(0, items); + } + return allItems; + } + + public bool CreateUsersInventory(Guid rawUserID) + { + UUID userID = new UUID(rawUserID); + + + return m_InventoryService.CreateUserInventory(userID); + } + + public List GetActiveGestures(Guid rawUserID) + { + UUID userID = new UUID(rawUserID); + + return m_InventoryService.GetActiveGestures(userID); + } + + public List GetInventorySkeleton(Guid rawUserID) + { + UUID userID = new UUID(rawUserID); + return m_InventoryService.GetInventorySkeleton(userID); + } + + #endregion + + /// + /// Check that the source of an inventory request is one that we trust. + /// + /// + /// + public bool CheckTrustSource(IPEndPoint peer) + { + if (m_doLookup) + { + m_log.InfoFormat("[INVENTORY IN CONNECTOR]: Checking trusted source {0}", peer); + UriBuilder ub = new UriBuilder(m_userserver_url); + IPAddress[] uaddrs = Dns.GetHostAddresses(ub.Host); + foreach (IPAddress uaddr in uaddrs) + { + if (uaddr.Equals(peer.Address)) + { + return true; + } + } + + m_log.WarnFormat( + "[INVENTORY IN CONNECTOR]: Rejecting request since source {0} was not in the list of trusted sources", + peer); + + return false; + } + else + { + return true; + } + } + + /// + /// Check that the source of an inventory request for a particular agent is a current session belonging to + /// that agent. + /// + /// + /// + /// + public bool CheckAuthSession(string session_id, string avatar_id) + { + if (m_doLookup) + { + m_log.InfoFormat("[INVENTORY IN CONNECTOR]: checking authed session {0} {1}", session_id, avatar_id); + + //if (m_session_cache.getCachedSession(session_id, avatar_id) == null) + //{ + // cache miss, ask userserver + Hashtable requestData = new Hashtable(); + requestData["avatar_uuid"] = avatar_id; + requestData["session_id"] = session_id; + ArrayList SendParams = new ArrayList(); + SendParams.Add(requestData); + XmlRpcRequest UserReq = new XmlRpcRequest("check_auth_session", SendParams); + XmlRpcResponse UserResp = UserReq.Send(m_userserver_url, 3000); + + Hashtable responseData = (Hashtable)UserResp.Value; + if (responseData.ContainsKey("auth_session") && responseData["auth_session"].ToString() == "TRUE") + { + m_log.Info("[INVENTORY IN CONNECTOR]: got authed session from userserver"); + //// add to cache; the session time will be automatically renewed + //m_session_cache.Add(session_id, avatar_id); + return true; + } + //} + //else + //{ + // // cache hits + // m_log.Info("[GRID AGENT INVENTORY]: got authed session from cache"); + // return true; + //} + + m_log.Warn("[INVENTORY IN CONNECTOR]: unknown session_id, request rejected"); + return false; + } + else + { + return true; + } + } + + } +}