diff --git a/OpenSim/Data/IFSAssetData.cs b/OpenSim/Data/IFSAssetData.cs
new file mode 100644
index 0000000000..8751dc08ab
--- /dev/null
+++ b/OpenSim/Data/IFSAssetData.cs
@@ -0,0 +1,47 @@
+/*
+ * 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.Collections.Generic;
+using OpenMetaverse;
+using OpenSim.Framework;
+
+namespace OpenSim.Data
+{
+ public delegate string FSStoreDelegate(AssetBase asset, bool force);
+
+ public interface IFSAssetDataPlugin : IPlugin
+ {
+ bool[] AssetsExist(UUID[] uuids);
+ void Initialise(string connect, string realm, int SkipAccessTimeDays);
+ bool Delete(string id);
+
+ AssetMetadata Get(string id, out string hash);
+ bool Store(AssetMetadata metadata, string hash);
+ void Import(string conn, string table, int start, int count, bool force, FSStoreDelegate store);
+ int Count();
+ }
+}
diff --git a/OpenSim/Data/MySQL/MySQLFSAssetData.cs b/OpenSim/Data/MySQL/MySQLFSAssetData.cs
index 4d7a395c29..19e23b5827 100644
--- a/OpenSim/Data/MySQL/MySQLFSAssetData.cs
+++ b/OpenSim/Data/MySQL/MySQLFSAssetData.cs
@@ -29,36 +29,76 @@ using System;
using System.Reflection;
using System.Collections.Generic;
using System.Data;
-using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using log4net;
using MySql.Data.MySqlClient;
-using System.Data;
using OpenMetaverse;
namespace OpenSim.Data.MySQL
{
- public delegate string StoreDelegate(AssetBase asset, bool force);
-
- public class FSAssetConnectorData
+ public class MySQLFSAssetData : IFSAssetDataPlugin
{
- private static readonly ILog m_log =
- LogManager.GetLogger(
- MethodBase.GetCurrentMethod().DeclaringType);
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected MySqlConnection m_Connection = null;
protected string m_ConnectionString;
protected string m_Table;
protected Object m_connLock = new Object();
- public FSAssetConnectorData(string connectionString, string table)
- {
- m_ConnectionString = connectionString;
- m_Table = table;
+ ///
+ /// Number of days that must pass before we update the access time on an asset when it has been fetched
+ /// Config option to change this is "DaysBetweenAccessTimeUpdates"
+ ///
+ private int DaysBetweenAccessTimeUpdates = 0;
- OpenDatabase();
+ protected virtual Assembly Assembly
+ {
+ get { return GetType().Assembly; }
}
+
+ public MySQLFSAssetData()
+ {
+ }
+
+ #region IPlugin Members
+
+ public string Version { get { return "1.0.0.0"; } }
+
+ // Loads and initialises the MySQL storage plugin and checks for migrations
+ public void Initialise(string connect, string realm, int UpdateAccessTime)
+ {
+ m_ConnectionString = connect;
+ m_Table = realm;
+
+ DaysBetweenAccessTimeUpdates = UpdateAccessTime;
+
+ try
+ {
+ OpenDatabase();
+
+ Migration m = new Migration(m_Connection, Assembly, "FSAssetStore");
+ m.Update();
+ }
+ catch (MySqlException e)
+ {
+ m_log.ErrorFormat("[FSASSETS]: Can't connect to database: {0}", e.Message.ToString());
+ }
+ }
+
+ public void Initialise()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Dispose() { }
+
+ public string Name
+ {
+ get { return "MySQL FSAsset storage engine"; }
+ }
+
+ #endregion
private bool OpenDatabase()
{
@@ -126,13 +166,15 @@ namespace OpenSim.Data.MySQL
}
}
+ #region IFSAssetDataPlugin Members
+
public AssetMetadata Get(string id, out string hash)
{
hash = String.Empty;
MySqlCommand cmd = new MySqlCommand();
- cmd.CommandText = String.Format("select id, name, description, type, hash, create_time, asset_flags from {0} where id = ?id", m_Table);
+ cmd.CommandText = String.Format("select id, name, description, type, hash, create_time, access_time, asset_flags from {0} where id = ?id", m_Table);
cmd.Parameters.AddWithValue("?id", id);
IDataReader reader = ExecuteReader(cmd);
@@ -158,17 +200,29 @@ namespace OpenSim.Data.MySQL
meta.CreationDate = Util.ToDateTime(Convert.ToInt32(reader["create_time"]));
meta.Flags = (AssetFlags)Convert.ToInt32(reader["asset_flags"]);
+ int AccessTime = Convert.ToInt32(reader["access_time"]);
+
reader.Close();
- cmd.CommandText = String.Format("update {0} set access_time = UNIX_TIMESTAMP() where id = ?id", m_Table);
-
- cmd.ExecuteNonQuery();
+ UpdateAccessTime(AccessTime, cmd);
FreeCommand(cmd);
return meta;
}
+ private void UpdateAccessTime(int AccessTime, MySqlCommand cmd)
+ {
+ // Reduce DB work by only updating access time if asset hasn't recently been accessed
+ // 0 By Default, Config option is "DaysBetweenAccessTimeUpdates"
+ if (DaysBetweenAccessTimeUpdates > 0 && (DateTime.UtcNow - Utils.UnixTimeToDateTime(AccessTime)).TotalDays < DaysBetweenAccessTimeUpdates)
+ return;
+
+ cmd.CommandText = String.Format("UPDATE {0} SET `access_time` = UNIX_TIMESTAMP() WHERE `id` = ?id", m_Table);
+
+ cmd.ExecuteNonQuery();
+ }
+
protected void FreeCommand(MySqlCommand cmd)
{
MySqlConnection c = cmd.Connection;
@@ -214,7 +268,7 @@ namespace OpenSim.Data.MySQL
catch(Exception e)
{
m_log.Error("[FSAssets] Failed to store asset with ID " + meta.ID);
- m_log.Error(e.ToString());
+ m_log.Error(e.ToString());
return false;
}
}
@@ -272,20 +326,21 @@ namespace OpenSim.Data.MySQL
return count;
}
- public void Delete(string id)
+ public bool Delete(string id)
{
- MySqlCommand cmd = m_Connection.CreateCommand();
+ using (MySqlCommand cmd = m_Connection.CreateCommand())
+ {
+ cmd.CommandText = String.Format("delete from {0} where id = ?id", m_Table);
- cmd.CommandText = String.Format("delete from {0} where id = ?id", m_Table);
+ cmd.Parameters.AddWithValue("?id", id);
- cmd.Parameters.AddWithValue("?id", id);
+ ExecuteNonQuery(cmd);
+ }
- ExecuteNonQuery(cmd);
-
- cmd.Dispose();
+ return true;
}
- public void Import(string conn, string table, int start, int count, bool force, StoreDelegate store)
+ public void Import(string conn, string table, int start, int count, bool force, FSStoreDelegate store)
{
MySqlConnection importConn;
@@ -353,5 +408,7 @@ namespace OpenSim.Data.MySQL
MainConsole.Instance.Output(String.Format("Import done, {0} assets imported", imported));
}
+
+ #endregion
}
}
diff --git a/OpenSim/Data/MySQL/Resources/FSAssetStore.migrations b/OpenSim/Data/MySQL/Resources/FSAssetStore.migrations
new file mode 100644
index 0000000000..87d08c6c23
--- /dev/null
+++ b/OpenSim/Data/MySQL/Resources/FSAssetStore.migrations
@@ -0,0 +1,18 @@
+# -----------------
+:VERSION 1
+
+BEGIN;
+
+CREATE TABLE `fsassets` (
+ `id` char(36) NOT NULL,
+ `name` varchar(64) NOT NULL DEFAULT '',
+ `description` varchar(64) NOT NULL DEFAULT '',
+ `type` int(11) NOT NULL,
+ `hash` char(80) NOT NULL,
+ `create_time` int(11) NOT NULL DEFAULT '0',
+ `access_time` int(11) NOT NULL DEFAULT '0',
+ `asset_flags` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+COMMIT;
\ No newline at end of file
diff --git a/OpenSim/Services/FSAssetService/FSAssetService.cs b/OpenSim/Services/FSAssetService/FSAssetService.cs
index 3662e279c6..8276f33646 100644
--- a/OpenSim/Services/FSAssetService/FSAssetService.cs
+++ b/OpenSim/Services/FSAssetService/FSAssetService.cs
@@ -33,6 +33,7 @@ using System.IO.Compression;
using System.Text;
using System.Threading;
using System.Reflection;
+using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Server.Base;
@@ -47,9 +48,7 @@ namespace OpenSim.Services.FSAssetService
{
public class FSAssetConnector : ServiceBase, IAssetService
{
- private static readonly ILog m_log =
- LogManager.GetLogger(
- MethodBase.GetCurrentMethod().DeclaringType);
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
static System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
static SHA256CryptoServiceProvider SHA256 = new SHA256CryptoServiceProvider();
@@ -64,9 +63,7 @@ namespace OpenSim.Services.FSAssetService
}
protected IAssetLoader m_AssetLoader = null;
- protected string m_ConnectionString;
- protected FSAssetConnectorData m_DataConnector = null;
- protected string m_FsckProgram;
+ protected IFSAssetDataPlugin m_DataConnector = null;
protected IAssetService m_FallbackService;
protected Thread m_WriterThread;
protected Thread m_StatsThread;
@@ -78,7 +75,6 @@ namespace OpenSim.Services.FSAssetService
protected int m_missingAssets = 0;
protected int m_missingAssetsFS = 0;
protected string m_FSBase;
- protected string m_Realm;
public FSAssetConnector(IConfigSource config)
: this(config, "AssetService")
@@ -87,8 +83,6 @@ namespace OpenSim.Services.FSAssetService
public FSAssetConnector(IConfigSource config, string configName) : base(config)
{
- m_FsckProgram = string.Empty;
-
MainConsole.Instance.Commands.AddCommand("fs", false,
"show assets", "show assets", "Show asset stats",
HandleShowAssets);
@@ -109,35 +103,63 @@ namespace OpenSim.Services.FSAssetService
HandleImportAssets);
IConfig assetConfig = config.Configs[configName];
+
if (assetConfig == null)
- {
throw new Exception("No AssetService configuration");
- }
- m_ConnectionString = assetConfig.GetString("ConnectionString", string.Empty);
- if (m_ConnectionString == string.Empty)
+ // Get Database Connector from Asset Config (If present)
+ string dllName = assetConfig.GetString("StorageProvider", string.Empty);
+ string m_ConnectionString = assetConfig.GetString("ConnectionString", string.Empty);
+ string m_Realm = assetConfig.GetString("Realm", "fsassets");
+
+ int SkipAccessTimeDays = assetConfig.GetInt("DaysBetweenAccessTimeUpdates", 0);
+
+ // If not found above, fallback to Database defaults
+ IConfig dbConfig = config.Configs["DatabaseService"];
+
+ if (dbConfig != null)
{
- throw new Exception("Missing database connection string");
+ if (dllName == String.Empty)
+ dllName = dbConfig.GetString("StorageProvider", String.Empty);
+
+ if (m_ConnectionString == String.Empty)
+ m_ConnectionString = dbConfig.GetString("ConnectionString", String.Empty);
}
- m_Realm = assetConfig.GetString("Realm", "fsassets");
+ // No databse connection found in either config
+ if (dllName.Equals(String.Empty))
+ throw new Exception("No StorageProvider configured");
- m_DataConnector = new FSAssetConnectorData(m_ConnectionString, m_Realm);
+ if (m_ConnectionString.Equals(String.Empty))
+ throw new Exception("Missing database connection string");
+
+ // Create Storage Provider
+ m_DataConnector = LoadPlugin(dllName);
+
+ if (m_DataConnector == null)
+ throw new Exception(string.Format("Could not find a storage interface in the module {0}", dllName));
+
+ // Initialize DB And perform any migrations required
+ m_DataConnector.Initialise(m_ConnectionString, m_Realm, SkipAccessTimeDays);
+
+ // Setup Fallback Service
string str = assetConfig.GetString("FallbackService", string.Empty);
+
if (str != string.Empty)
{
object[] args = new object[] { config };
m_FallbackService = LoadPlugin(str, args);
if (m_FallbackService != null)
{
- m_log.Info("[FALLBACK]: Fallback service loaded");
+ m_log.Info("[FSASSETS]: Fallback service loaded");
}
else
{
- m_log.Error("[FALLBACK]: Failed to load fallback service");
+ m_log.Error("[FSASSETS]: Failed to load fallback service");
}
}
+ // Setup directory structure including temp directory
m_SpoolDirectory = assetConfig.GetString("SpoolDirectory", "/tmp");
string spoolTmp = Path.Combine(m_SpoolDirectory, "spool");
@@ -147,7 +169,7 @@ namespace OpenSim.Services.FSAssetService
m_FSBase = assetConfig.GetString("BaseDirectory", String.Empty);
if (m_FSBase == String.Empty)
{
- m_log.ErrorFormat("[ASSET]: BaseDirectory not specified");
+ m_log.ErrorFormat("[FSASSETS]: BaseDirectory not specified");
throw new Exception("Configuration error");
}
@@ -156,14 +178,14 @@ namespace OpenSim.Services.FSAssetService
{
m_AssetLoader = LoadPlugin(loader);
string loaderArgs = assetConfig.GetString("AssetLoaderArgs", string.Empty);
- m_log.InfoFormat("[ASSET]: Loading default asset set from {0}", loaderArgs);
+ m_log.InfoFormat("[FSASSETS]: Loading default asset set from {0}", loaderArgs);
m_AssetLoader.ForEachDefaultXmlAsset(loaderArgs,
delegate(AssetBase a)
{
Store(a, false);
});
}
- m_log.Info("[ASSET]: FS asset service enabled");
+ m_log.Info("[FSASSETS]: FS asset service enabled");
m_WriterThread = new Thread(Writer);
m_WriterThread.Start();
@@ -184,7 +206,7 @@ namespace OpenSim.Services.FSAssetService
double avg = (double)m_readTicks / (double)m_readCount;
// if (avg > 10000)
// Environment.Exit(0);
- m_log.InfoFormat("[ASSET]: Read stats: {0} files, {1} ticks, avg {2:F2}, missing {3}, FS {4}", m_readCount, m_readTicks, (double)m_readTicks / (double)m_readCount, m_missingAssets, m_missingAssetsFS);
+ m_log.InfoFormat("[FSASSETS]: Read stats: {0} files, {1} ticks, avg {2:F2}, missing {3}, FS {4}", m_readCount, m_readTicks, (double)m_readTicks / (double)m_readCount, m_missingAssets, m_missingAssetsFS);
}
m_readCount = 0;
m_readTicks = 0;
@@ -196,7 +218,7 @@ namespace OpenSim.Services.FSAssetService
private void Writer()
{
- m_log.Info("[ASSET]: Writer started");
+ m_log.Info("[FSASSETS]: Writer started");
while (true)
{
@@ -236,7 +258,7 @@ namespace OpenSim.Services.FSAssetService
int totalTicks = System.Environment.TickCount - tickCount;
if (totalTicks > 0) // Wrap?
{
- m_log.InfoFormat("[ASSET]: Write cycle complete, {0} files, {1} ticks, avg {2:F2}", files.Length, totalTicks, (double)totalTicks / (double)files.Length);
+ m_log.InfoFormat("[FSASSETS]: Write cycle complete, {0} files, {1} ticks, avg {2:F2}", files.Length, totalTicks, (double)totalTicks / (double)files.Length);
}
}
@@ -326,13 +348,13 @@ namespace OpenSim.Services.FSAssetService
asset.Metadata.ContentType =
SLUtil.SLAssetTypeToContentType((int)asset.Type);
sha = GetSHA256Hash(asset.Data);
- m_log.InfoFormat("[FALLBACK]: Added asset {0} from fallback to local store", id);
+ m_log.InfoFormat("[FSASSETS]: Added asset {0} from fallback to local store", id);
Store(asset);
}
}
if (asset == null)
{
-// m_log.InfoFormat("[ASSET]: Asset {0} not found", id);
+ // m_log.InfoFormat("[FSASSETS]: Asset {0} not found", id);
m_missingAssets++;
}
return asset;
@@ -353,13 +375,13 @@ namespace OpenSim.Services.FSAssetService
asset.Metadata.ContentType =
SLUtil.SLAssetTypeToContentType((int)asset.Type);
sha = GetSHA256Hash(asset.Data);
- m_log.InfoFormat("[FALLBACK]: Added asset {0} from fallback to local store", id);
+ m_log.InfoFormat("[FSASSETS]: Added asset {0} from fallback to local store", id);
Store(asset);
}
}
if (asset == null)
m_missingAssetsFS++;
-// m_log.InfoFormat("[ASSET]: Asset {0}, hash {1} not found in FS", id, hash);
+ // m_log.InfoFormat("[FSASSETS]: Asset {0}, hash {1} not found in FS", id, hash);
else
return asset;
}
@@ -649,7 +671,7 @@ namespace OpenSim.Services.FSAssetService
{
count = Convert.ToInt32(args[4]);
}
- m_DataConnector.Import(conn, table, start, count, force, new StoreDelegate(Store));
+ m_DataConnector.Import(conn, table, start, count, force, new FSStoreDelegate(Store));
}
}