View of /trunk/Extensions/AssetServer.DistributedStorage/DistributedStorage.cs
Parent Directory
|
Revision Log
Revision 23 -
(download)
(annotate)
Tue Nov 11 16:44:14 2008 UTC (4 years, 6 months ago) by jhurliman
File size: 13942 byte(s)
Tue Nov 11 16:44:14 2008 UTC (4 years, 6 months ago) by jhurliman
File size: 13942 byte(s)
* Added AssetServer.DistributedStorage: Uses OpenDHT to store metadata and MySQL to store data. Still very experimental * Improved MySQL connection string config loading; will now try to load from .ini first * Changed all references to "assetid" in metadata to "id" * Updated to latest libomv trunk
/*
* Copyright (c) 2008 Intel Corporation
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* -- Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* -- Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* -- Neither the name of the Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using System.Data;
using System.Xml;
using MySql.Data.MySqlClient;
using CookComputing.XmlRpc;
using OpenDHTLib;
using ExtensionLoader;
using ExtensionLoader.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace AssetServer.Extensions
{
public static class DBConnString
{
public const string OLD_CONFIG_FILE = "AssetServer_Config.xml";
private static string connectionString;
/// <summary>
/// Parses the MySQL connection string out of either the asset server
/// .ini or a OpenSim-style .xml file and caches the result for future
/// requests
/// </summary>
public static string GetConnectionString(IniConfigSource configFile)
{
if (connectionString == null)
{
// Try parsing from the ini file first
try
{
// Load the extension list (and ordering) from our config file
IConfig extensionConfig = configFile.Configs["MySQL"];
connectionString = extensionConfig.GetString("database_connect", null);
}
catch (Exception) { }
if (connectionString == null)
{
try
{
// Parse the connection string out of the OpenSim asset server config file
XmlTextReader reader = new XmlTextReader(OLD_CONFIG_FILE);
if (reader.ReadToFollowing("Config"))
{
connectionString = reader.GetAttribute("database_connect");
// Force connection pooling on
MySqlConnectionStringBuilder builder = new MySqlConnectionStringBuilder(connectionString);
builder.Pooling = true;
connectionString = builder.ToString();
}
else
{
connectionString = String.Empty;
Logger.Log(String.Format(
"Cannot find MySQL configuration in {0}, make sure this file is in the same " +
"directory as the asset server and the database_connect attribute is correct",
OLD_CONFIG_FILE), Helpers.LogLevel.Error);
}
reader.Close();
}
catch (Exception)
{
connectionString = String.Empty;
}
}
}
return connectionString;
}
}
class DistributedStorage : IExtension<AssetServer>, IMetadataProvider, IDataProvider, ICreateAssetProvider
{
AssetServer server;
public DistributedStorage()
{
}
public void Start(AssetServer server)
{
this.server = server;
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
{
try
{
dbConnection.Open();
Logger.Log("Connected to MySQL backend: " + dbConnection.ServerVersion, Helpers.LogLevel.Info);
}
catch (MySqlException ex)
{
Logger.Log("Connection to MySQL backend failed: " + ex.Message, Helpers.LogLevel.Error);
}
}
}
public void Stop()
{
}
public bool TryFetchMetadata(UUID assetID, out Metadata metadata)
{
metadata = null;
byte[] data = null;
try
{
IOpenDHT proxy = (IOpenDHT)XmlRpcProxyGen.Create(typeof(IOpenDHT));
(proxy as XmlRpcClientProtocol).UseIndentation = false;
object[] objs = proxy.Get(assetID.GetBytes(), 1, new byte[0], "AssetServer.DistributedStorage");
if (objs != null && objs.Length == 2)
data = (byte[])((object[])objs[0])[0];
else
Logger.Log(String.Format("Metadata for asset {0} not found in OpenDHT", assetID), Helpers.LogLevel.Warning);
}
catch (Exception ex)
{
Logger.Log("OpenDHT metadata lookup failed: " + ex.Message, Helpers.LogLevel.Warning);
return false;
}
if (data != null)
{
metadata = new Metadata();
OSDMap map = (OSDMap)OSDParser.DeserializeLLSDXml(data);
metadata.AssetURL = map["asset_url"].AsString();
metadata.CreationDate = map["creation_date"].AsDate();
metadata.Description = map["description"].AsString();
if (map.ContainsKey("extra_data"))
metadata.ExtraData = (OSDMap)map["extra_data"];
metadata.ID = assetID;
metadata.Name = map["name"].AsString();
metadata.SHA1 = map["sha1"].AsBinary();
metadata.Temporary = map["temporary"].AsBoolean();
OpenMetaverse.Utils.EnumTryParse<AssetType>(map["type"].AsString(), out metadata.Type);
return true;
}
else
{
return false;
}
}
public void ForEach(Action<Metadata> action)
{
Logger.Log("ForEach() called on DistributedStorage which does not support enumeration",
Helpers.LogLevel.Warning);
}
public bool TryFetchData(UUID assetID, out byte[] assetData)
{
assetData = null;
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
{
try { dbConnection.Open(); }
catch (MySqlException ex)
{
Logger.Log("Connection to MySQL backend failed: " + ex.Message, Helpers.LogLevel.Error);
return false;
}
IDbCommand command = dbConnection.CreateCommand();
command.CommandText = String.Format("SELECT data FROM assets WHERE id='{0}'", assetID.ToString());
IDataReader reader = command.ExecuteReader();
if (reader.Read())
{
assetData = (byte[])reader.GetValue(0);
return true;
}
else
{
return false;
}
}
}
public bool TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID)
{
assetID = metadata.ID = UUID.Random();
return TryCreateAsset(metadata, assetData);
}
public bool TryCreateAsset(Metadata metadata, byte[] assetData)
{
// Generate the asset URL
metadata.AssetURL = String.Format("http://{0}:{1}/{2}/data", Dns.GetHostName(), server.HttpPort, metadata.ID);
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
{
try { dbConnection.Open(); }
catch (MySqlException ex)
{
Logger.Log("Connection to MySQL backend failed: " + ex.Message, Helpers.LogLevel.Error);
return false;
}
MySqlCommand command = new MySqlCommand(
"REPLACE INTO assets (name,description,assetType,local,temporary,data,id) VALUES " +
"(?name,?description,?assetType,?local,?temporary,?data,?id)",
dbConnection);
command.Parameters.AddWithValue("?name", metadata.Name);
command.Parameters.AddWithValue("?description", metadata.Description);
command.Parameters.AddWithValue("?assetType", (int)metadata.Type);
command.Parameters.AddWithValue("?local", 0);
command.Parameters.AddWithValue("?temporary", metadata.Temporary);
command.Parameters.AddWithValue("?data", assetData);
command.Parameters.AddWithValue("?id", metadata.ID.ToString());
int result = command.ExecuteNonQuery();
dbConnection.Close();
if (result == 1)
{
// Database insertion succeeded, insert the metadata into OpenDHT
OSDMap osdata = new OSDMap();
osdata["id"] = OSD.FromUUID(metadata.ID);
osdata["name"] = OSD.FromString(metadata.Name);
osdata["description"] = OSD.FromString(metadata.Description);
osdata["creation_date"] = OSD.FromDate(metadata.CreationDate);
osdata["type"] = OSD.FromString(metadata.Type.ToString());
osdata["sha1"] = OSD.FromBinary(metadata.SHA1);
osdata["temporary"] = OSD.FromBoolean(metadata.Temporary);
osdata["asset_url"] = OSD.FromString(metadata.AssetURL);
if (metadata.ExtraData != null)
osdata["extra_data"] = metadata.ExtraData;
byte[] serializedData = OSDParser.SerializeLLSDXmlBytes(osdata);
try
{
IOpenDHT proxy = (IOpenDHT)XmlRpcProxyGen.Create(typeof(IOpenDHT));
(proxy as XmlRpcClientProtocol).UseIndentation = false;
// 86,400 seconds == one day
result = proxy.Put(metadata.ID.GetBytes(), serializedData, 86400, "AssetServer.DistributedStorage");
switch (result)
{
case 0:
Logger.DebugLog("Metadata successfully inserted into OpenDHT");
return true;
case 1:
Logger.Log("Metadata was too large to fit in OpenDHT", Helpers.LogLevel.Error);
break;
case 2:
Logger.Log("OpenDHT had a temporary failure preventing the metadata from being uploaded",
Helpers.LogLevel.Error);
break;
default:
Logger.Log("Unknown OpenDHT error", Helpers.LogLevel.Error);
break;
}
}
catch (WebException ex)
{
Logger.Log("Failed to connect to OpenDHT: " + ex.Message, Helpers.LogLevel.Warning);
}
// Something went wrong, delete the asset from the database
try { dbConnection.Open(); }
catch (MySqlException mex)
{
Logger.Log("Connection to MySQL backend failed: " + mex.Message, Helpers.LogLevel.Error);
return false;
}
command = new MySqlCommand(String.Format("DELETE FROM assets WHERE id='{0}'", metadata.ID), dbConnection);
result = command.ExecuteNonQuery();
if (result == 1)
Logger.DebugLog("Asset removed from the database");
else
Logger.Log("Failed to remove the asset from the database", Helpers.LogLevel.Error);
return false;
}
}
return false;
}
}
}
| ViewVC Help | |
| Powered by ViewVC 1.0.0 |

