From cdd5dba5b0d327e312a041c5e8b30d6c3a4b9068 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sat, 24 Oct 2020 17:39:30 +0100 Subject: [PATCH] some changes; let regions have own assets connector, make the services connector one be like ther older simpler one (as 0.8x). still not good --- .../CoreModules/Asset/FlotsamAssetCache.cs | 5 + .../Asset/HGAssetBroker.cs | 21 +- .../Asset/LocalAssetServiceConnector.cs | 20 +- .../Asset/RegionBaseAssetServicesConnector.cs | 1037 +++++++++++++++++ .../Asset/RemoteAssetServiceConnector.cs | 27 +- OpenSim/Services/AssetService/AssetService.cs | 5 + .../Services/AssetService/XAssetService.cs | 5 + .../Asset/AssetServicesConnector.cs | 675 +---------- .../Asset/HGAssetServiceConnector.cs | 6 + .../Services/FSAssetService/FSAssetService.cs | 5 + .../HypergridService/HGAssetService.cs | 5 + .../HypergridService/HGRemoteAssetService.cs | 5 + OpenSim/Services/Interfaces/IAssetService.cs | 1 + 13 files changed, 1138 insertions(+), 679 deletions(-) create mode 100644 OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RegionBaseAssetServicesConnector.cs diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index ee6ba153b5..605ca41e89 100755 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -562,6 +562,11 @@ namespace OpenSim.Region.CoreModules.Asset return asset; } + public AssetBase Get(string id, string ForeignAssetService) + { + return null; + } + public bool Get(string id, out AssetBase asset) { asset = null; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs index 7eb1958cd1..07911ba809 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs @@ -33,7 +33,6 @@ using System.Reflection; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; -using OpenSim.Server.Base; using OpenSim.Services.Connectors; using OpenSim.Services.Interfaces; @@ -41,12 +40,10 @@ using OpenSim.Services.Interfaces; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "HGAssetBroker")] - public class HGAssetBroker : AssetServicesConnector, ISharedRegionModule, IAssetService + public class HGAssetBroker : RegionBaseAssetServicesConnector, ISharedRegionModule, IAssetService { private static readonly ILog m_log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); - private IAssetCache m_Cache = null; - private Scene m_aScene; private bool m_Enabled = false; @@ -70,7 +67,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset Initialise(config); } - public override void Initialise(IConfigSource source) + public void Initialise(IConfigSource source) { IConfig moduleConfig = source.Configs["Modules"]; if (moduleConfig != null) @@ -78,17 +75,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset string name = moduleConfig.GetString("AssetServices", ""); if (name == Name) { - IConfig assetConfig = source.Configs["AssetService"]; - if (assetConfig == null) - { - m_log.Error("[HG ASSET CONNECTOR]: AssetService missing from OpenSim.ini"); - return; - } - IConfig hgConfig = source.Configs["HGAssetService"]; - m_AssetPerms = new AssetPermissions(hgConfig); // it's ok if arg is null + if(hgConfig != null) + m_AssetPerms = new AssetPermissions(hgConfig); // it's ok if arg is null - base.Initialise(source); + baseInitialise(source); m_Enabled = true; m_log.Info("[HG ASSET CONNECTOR]: HG asset broker enabled"); } @@ -128,8 +119,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset if (!(m_Cache is ISharedRegionModule)) m_Cache = null; - else - SetCache(m_Cache); } if (m_Cache != null) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs index c8a70e10f7..7000b1509b 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs @@ -131,19 +131,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset m_Cache = null; } - if (m_Cache != null) - { - m_log.DebugFormat( - "[LOCAL ASSET SERVICES CONNECTOR]: Enabled asset connector with caching for region {0}", + if (m_Cache == null) + m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Enabled asset connector with caching for region {0}", scene.RegionInfo.RegionName); - } else - { - // Short-circuit directly to storage layer. This ends up storing temporary and local assets. - // - scene.UnregisterModuleInterface(this); - scene.RegisterModuleInterface(m_AssetService); - } + m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Enabled asset connector without caching for region {0}", + scene.RegionInfo.RegionName); } public AssetBase Get(string id) @@ -168,6 +161,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset return asset; } + public AssetBase Get(string id, string ForeignAssetService) + { + return null; + } + public AssetBase GetCached(string id) { // m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Cache request for {0}", id); diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RegionBaseAssetServicesConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RegionBaseAssetServicesConnector.cs new file mode 100644 index 0000000000..b7503fbbdb --- /dev/null +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RegionBaseAssetServicesConnector.cs @@ -0,0 +1,1037 @@ +/* + * 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 System; +using System.Threading; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.IO; +using System.Reflection; +using System.Timers; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Monitoring; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class RegionBaseAssetServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + const int MAXSENDRETRIESLEN = 30; + protected IServiceAuth m_Auth; + protected IAssetCache m_Cache = null; + private List[] m_sendRetries; + private List[] m_sendCachedRetries; + private System.Timers.Timer m_retryTimer; + + public readonly object ConnectorLock = new object(); + private string m_ServerURI = string.Empty; + private int m_retryCounter; + private bool m_inRetries; + + private int m_maxAssetRequestConcurrency = 8; + + private delegate void AssetRetrievedEx(AssetBase asset); + + // Keeps track of concurrent requests for the same asset, so that it's only loaded once. + // Maps: Asset ID -> Handlers which will be called when the asset has been loaded + + private Dictionary> m_AssetHandlers = new Dictionary>(); + + private Dictionary m_UriMap; + + private Thread[] m_fetchThreads; + + public int MaxAssetRequestConcurrency + { + get { return m_maxAssetRequestConcurrency; } + set { m_maxAssetRequestConcurrency = value; } + } + + public void baseInitialise(IConfigSource source) + { + IConfig assetConfig = source.Configs["AssetService"]; + if (assetConfig == null) + { + m_log.Error("[ASSET CONNECTOR]: AssetService missing from OpenSim.ini"); + throw new Exception("Asset connector init error"); + } + + IConfig netConfig = source.Configs["Network"]; + m_ServerURI = assetConfig.GetString("AssetServerURI", string.Empty); + if (string.IsNullOrEmpty(m_ServerURI)) + { + if(netConfig != null) + m_ServerURI = netConfig.GetString("asset_server_url", string.Empty); + } + if (string.IsNullOrEmpty(m_ServerURI)) + { + m_log.Error("[ASSET CONNECTOR]: AssetServerURI not defined in section AssetService"); + throw new Exception("Asset connector init error"); + } + + OSHHTPHost m_GridAssetsURL = new OSHHTPHost(m_ServerURI, true); + if(!m_GridAssetsURL.IsResolvedHost) + { + m_log.Error("[ASSET CONNECTOR]: Could not parse or resolve AssetServerURI"); + throw new Exception("Asset connector init error"); + } + + m_ServerURI = m_GridAssetsURL.URI; + + string authType = assetConfig.GetString("AuthType", string.Empty); + if (string.IsNullOrEmpty(authType)) + { + if (netConfig != null) + authType = netConfig.GetString("AuthType", string.Empty); + } + switch (authType) + { + case "BasicHttpAuthentication": + m_Auth = new BasicHttpAuthentication(source, "AssetService"); + break; + } + + bool usemaps = assetConfig.GetBoolean("AssetServerIsMultiple", false); + + if(usemaps) + { + m_UriMap = new Dictionary(); + for (int i = 0; i < 256; i++) + { + string prefix = i.ToString("x2"); + string groupHost = assetConfig.GetString("AssetServerHost_" + prefix, string.Empty); + if(string.IsNullOrEmpty(groupHost)) + m_UriMap[prefix] = m_ServerURI; + else + { + OSHHTPHost other = new OSHHTPHost(groupHost, true); + if(!other.IsResolvedHost) + { + m_log.Error("[ASSET CONNECTOR]: Could not parse or resolve AssetServerHost_" + prefix); + throw new Exception("Asset connector init error"); + } + m_UriMap[prefix] = other.URI; + } + } + } + else + m_UriMap = null; + + m_sendRetries = new List[MAXSENDRETRIESLEN]; + m_sendCachedRetries = new List[MAXSENDRETRIESLEN]; + + m_retryTimer = new System.Timers.Timer(); + m_retryTimer.Elapsed += new ElapsedEventHandler(retryCheck); + m_retryTimer.AutoReset = true; + m_retryTimer.Interval = 60000; + + m_fetchThreads = new Thread[3]; + + for (int i = 0 ; i < m_fetchThreads.Length; i++) + { + m_fetchThreads[i] = WorkManager.StartThread(AssetRequestProcessor, string.Format("GetAssetsWorker{0}", i)); + } + } + + private string MapServer(string id) + { + if (m_UriMap == null) + return m_ServerURI; + + string prefix = id.Substring(0, 2).ToLower(); + + if (m_UriMap.TryGetValue(prefix, out string host)) + return host; + + return m_UriMap["00"]; + } + + protected void retryCheck(object source, ElapsedEventArgs e) + { + lock(m_sendRetries) + { + if(m_inRetries) + return; + m_inRetries = true; + } + + m_retryCounter++; + if(m_retryCounter >= 61 ) // avoid overflow 60 is max in use below + m_retryCounter = 1; + + int inUse = 0; + int nextlevel; + int timefactor; + if(m_Cache == null) + { + List retrylist; + // we need to go down + for(int i = MAXSENDRETRIESLEN - 1; i >= 0; i--) + { + lock(m_sendRetries) + retrylist = m_sendRetries[i]; + + if(retrylist == null) + continue; + + inUse++; + nextlevel = i + 1; + + //We exponentially fall back on frequency until we reach one attempt per hour + //The net result is that we end up in the queue for roughly 24 hours.. + //24 hours worth of assets could be a lot, so the hope is that the region admin + //will have gotten the asset connector back online quickly! + if(i == 0) + timefactor = 1; + else + { + timefactor = 1 << nextlevel; + if (timefactor > 60) + timefactor = 60; + } + + if(m_retryCounter < timefactor) + continue; // to update inUse; + + if (m_retryCounter % timefactor != 0) + continue; + + // a list to retry + lock(m_sendRetries) + m_sendRetries[i] = null; + + // we are the only ones with a copy of this retrylist now + foreach(AssetBase ass in retrylist) + retryStore(ass, nextlevel); + } + } + else + { + List retrylist; + + for (int i = MAXSENDRETRIESLEN - 1; i >= 0; i--) + { + lock (m_sendRetries) + retrylist = m_sendCachedRetries[i]; + + if (retrylist == null) + continue; + + inUse++; + nextlevel = i + 1; + + //We exponentially fall back on frequency until we reach one attempt per hour + //The net result is that we end up in the queue for roughly 24 hours.. + //24 hours worth of assets could be a lot, so the hope is that the region admin + //will have gotten the asset connector back online quickly! + if (i == 0) + timefactor = 1; + else + { + timefactor = 1 << nextlevel; + if (timefactor > 60) + timefactor = 60; + } + + if (m_retryCounter < timefactor) + continue; // to update inUse; + + if (m_retryCounter % timefactor != 0) + continue; + + // a list to retry + lock (m_sendRetries) + m_sendCachedRetries[i] = null; + + // we are the only ones with a copy of this retrylist now + foreach (string id in retrylist) + retryCachedStore(id, nextlevel); + } + } + + lock (m_sendRetries) + { + if(inUse == 0 ) + m_retryTimer.Stop(); + + m_inRetries = false; + } + } + + public void SetCache(IAssetCache cache) + { + m_Cache = cache; + } + + public AssetBase GetCached(string id) + { + AssetBase asset = null; + if (m_Cache != null) + { + m_Cache.Get(id, out asset); + } + + return asset; + } + + public virtual AssetBase Get(string id) + { + AssetBase asset = null; + if (m_Cache != null) + { + if (!m_Cache.Get(id, out asset)) + return null; + } + + if (asset == null || asset.Data == null || asset.Data.Length == 0) + { + string uri = MapServer(id) + "/assets/" + id; + + asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, m_Auth); + if (m_Cache != null) + { + if (asset != null) + m_Cache.Cache(asset); + else + m_Cache.CacheNegative(id); + } + } + return asset; + } + + public AssetBase Get(string id, string ForeignAssetService) + { + // assumes id and ForeignAssetService are valid and resolved + AssetBase asset = null; + if (m_Cache != null) + { + m_Cache.Get(id, out asset); // negative cache is a fail on HG + } + + if (asset == null) + { + IServiceAuth auth = null; + if (ForeignAssetService.Equals(m_ServerURI)) + { + ForeignAssetService = MapServer(id) + "/assets/" + id; + auth = m_Auth; + } + else + ForeignAssetService = ForeignAssetService + "/assets/" + id; + asset = SynchronousRestObjectRequester.MakeRequest("GET", ForeignAssetService, 0, auth); + } + return asset; + } + + public AssetBase GetForeign(string id) + { + int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); + if (type < 0) + return null; + + AssetBase asset = null; + if (m_Cache != null) + { + //if (!m_Cache.Get(uuidstr, out asset)) + // return null; + m_Cache.Get(uuidstr, out asset); // negative cache is a fail on HG + } + + if (asset == null || asset.Data == null || asset.Data.Length == 0) + { + IServiceAuth auth = null; + if (type == 0) + { + uri = MapServer(uuidstr) + "/assets/" + uuidstr; + auth = m_Auth; + } + else + uri = uri + "/assets/" + uuidstr; + + asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, auth); + } + return asset; + } + + public virtual AssetMetadata GetMetadata(string id) + { + if (m_Cache != null) + { + AssetBase fullAsset; + if (!m_Cache.Get(id, out fullAsset)) + return null; + + if (fullAsset != null) + return fullAsset.Metadata; + } + + string uri = MapServer(id) + "/assets/" + id + "/metadata"; + + AssetMetadata asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, m_Auth); + return asset; + } + + public AssetMetadata GetForeignMetadata(string id) + { + int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); + if (type < 0) + return null; + + if (m_Cache != null) + { + AssetBase fullAsset; + if (!m_Cache.Get(uuidstr, out fullAsset)) + return null; + + if (fullAsset != null) + return fullAsset.Metadata; + } + + IServiceAuth auth = null; + if (type == 0) + { + auth = m_Auth; + uri = MapServer(uuidstr) + "/assets/" + uuidstr + "/metadata"; + } + else + uri = uri + "/assets/" + uuidstr + "/metadata"; + + AssetMetadata asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, auth); + return asset; + } + + public virtual byte[] GetData(string id) + { + if (m_Cache != null) + { + if (!m_Cache.Get(id, out AssetBase fullAsset)) + return null; + + if (fullAsset != null) + return fullAsset.Data; + } + + string uri = MapServer(id); + + using (RestClient rc = new RestClient(uri)) + { + rc.AddResourcePath("assets/" + id + "/Data"); + rc.RequestMethod = "GET"; + + using (MemoryStream s = rc.Request(m_Auth)) + { + if (s == null || s.Length == 0) + return null; + return s.ToArray(); + } + } + } + + public byte[] GetForeignData(string id) + { + int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); + if (type < 0) + return null; + + if (m_Cache != null) + { + if (!m_Cache.Get(uuidstr, out AssetBase fullAsset)) + return null; + + if (fullAsset != null) + return fullAsset.Data; + } + + IServiceAuth auth = null; + if (type == 0) + { + uri = MapServer(uuidstr); + auth = m_Auth; + } + + using (RestClient rc = new RestClient(uri)) + { + rc.AddResourcePath("assets/" + id + "/Data"); + rc.RequestMethod = "GET"; + + using (MemoryStream s = rc.Request(auth)) + { + if (s == null || s.Length == 0) + return null; + return s.ToArray(); + } + } + } + + private class QueuedAssetRequest + { + public string uri; + public string id; + public IServiceAuth auth; + } + + public virtual bool Get(string id, object sender, AssetRetrieved handler) + { + AssetBase asset = null; + if (m_Cache != null) + { + if (!m_Cache.Get(id, out asset)) + return false; + } + + if (asset == null) + { + string uri = MapServer(id) + "/assets/" + id; + + lock (m_AssetHandlers) + { + AssetRetrievedEx handlerEx = new AssetRetrievedEx(delegate (AssetBase _asset) { handler(id, sender, _asset); }); + + List handlers; + if (m_AssetHandlers.TryGetValue(id, out handlers)) + { + // Someone else is already loading this asset. It will notify our handler when done. + handlers.Add(handlerEx); + return true; + } + + handlers = new List(); + handlers.Add(handlerEx); + + m_AssetHandlers.Add(id, handlers); + + QueuedAssetRequest request = new QueuedAssetRequest(); + request.id = id; + request.uri = uri; + request.auth = m_Auth; + m_requestQueue.Add(request); + } + } + else + { + if (asset != null && (asset.Data == null || asset.Data.Length == 0)) + asset = null; + handler(id, sender, asset); + } + + return true; + } + + public bool GetForeign(string id, object sender, AssetRetrieved handler) + { + int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); + if (type < 0) + return false; + + AssetBase asset = null; + if (m_Cache != null) + { + m_Cache.Get(uuidstr, out asset); + } + + if (asset == null) + { + IServiceAuth auth = null; + if (type == 0) + { + uri = MapServer(uuidstr) + "/assets/" + uuidstr; + auth = m_Auth; + } + else + uri = uri + "/assets/" + uuidstr; + + lock (m_AssetHandlers) + { + AssetRetrievedEx handlerEx = new AssetRetrievedEx(delegate (AssetBase _asset) { handler(id, sender, _asset); }); + + List handlers; + if (m_AssetHandlers.TryGetValue(id, out handlers)) + { + // Someone else is already loading this asset. It will notify our handler when done. + handlers.Add(handlerEx); + return true; + } + + handlers = new List(); + handlers.Add(handlerEx); + + m_AssetHandlers.Add(id, handlers); + + QueuedAssetRequest request = new QueuedAssetRequest(); + request.id = id; + request.uri = uri; + request.auth = m_Auth; + m_requestQueue.Add(request); + } + } + else + { + if (asset != null && (asset.Data == null || asset.Data.Length == 0)) + asset = null; + handler(id, sender, asset); + } + + return true; + } + + private BlockingCollection m_requestQueue = new BlockingCollection(); + private void AssetRequestProcessor() + { + QueuedAssetRequest r; + + while (true) + { + if(!m_requestQueue.TryTake(out r, 4500) || r == null) + { + Watchdog.UpdateThread(); + continue; + } + + Watchdog.UpdateThread(); + string id = r.id; + + try + { + AssetBase a = SynchronousRestObjectRequester.MakeRequest("GET", r.uri, 0, 30000, r.auth); + + if (a != null && m_Cache != null) + m_Cache.Cache(a); + + List handlers; + lock (m_AssetHandlers) + { + handlers = m_AssetHandlers[id]; + m_AssetHandlers.Remove(id); + } + + if(handlers != null) + { + Util.FireAndForget(x => + { + foreach (AssetRetrievedEx h in handlers) + { + try { h.Invoke(a); } + catch { } + } + handlers.Clear(); + }); + } + } + catch { } + } + } + + public virtual bool[] AssetsExist(string[] ids) + { + string uri = m_ServerURI + "/get_assets_exist"; + + bool[] exist = null; + try + { + exist = SynchronousRestObjectRequester.MakeRequest("POST", uri, ids, m_Auth); + } + catch (Exception) + { + // This is most likely to happen because the server doesn't support this function, + // so just silently return "doesn't exist" for all the assets. + } + + if (exist == null) + exist = new bool[ids.Length]; + + return exist; + } + + private struct AssetAndIndex + { + public string assetID; + public int index; + public IServiceAuth auth; + + public AssetAndIndex(string assetID, int index, IServiceAuth auth) + { + this.assetID = assetID; + this.index = index; + this.auth = auth; + } + } + + public bool[] ForeignAssetsExist(string[] ids) + { + bool[] exist = new bool[ids.Length]; + + var url2assets = new Dictionary>(); + + for (int i = 0; i < ids.Length; i++) + { + int ltype = Util.ParseForeignAssetID(ids[i], out string lurl, out string luuidstr); + if (ltype > 0) + { + IServiceAuth auth = null; + + if (ltype == 0) + { + lurl = m_ServerURI; + auth = m_Auth; + } + + List < AssetAndIndex > lst; + if (!url2assets.TryGetValue(lurl, out lst)) + { + lst = new List(); + url2assets.Add(lurl, lst); + } + lst.Add(new AssetAndIndex(luuidstr, i, auth)); + } + } + + // Query each of the servers in turn + foreach (KeyValuePair> kvp in url2assets) + { + List curAssets = kvp.Value; + string[] assetIDs = new string[curAssets.Count]; + IServiceAuth auth = curAssets[0].auth; + for (int i = 0; i < assetIDs.Length;++i) + assetIDs[i] = curAssets[i].assetID; + + string uri = kvp.Key + "/get_assets_exist"; + + bool[] curExist = null; + try + { + curExist = SynchronousRestObjectRequester.MakeRequest("POST", uri, assetIDs, auth); + } + catch (Exception) + { + // This is most likely to happen because the server doesn't support this function, + // so just silently return "doesn't exist" for all the assets. + } + + if(curExist != null) + { + int i = 0; + foreach (AssetAndIndex ai in curAssets) + { + exist[ai.index] = curExist[i]; + ++i; + } + } + } + + return exist; + } + + string stringUUIDZero = UUID.Zero.ToString(); + + public virtual string Store(AssetBase asset) + { + // Have to assign the asset ID here. This isn't likely to + // trigger since current callers don't pass emtpy IDs + // We need the asset ID to route the request to the proper + // cluster member, so we can't have the server assign one. + if (asset.ID == string.Empty || asset.ID == stringUUIDZero) + { + if (asset.FullID == UUID.Zero) + { + asset.FullID = UUID.Random(); + } + m_log.WarnFormat("[Assets] Zero ID: {0}",asset.Name); + asset.ID = asset.FullID.ToString(); + } + + if (asset.FullID == UUID.Zero) + { + UUID uuid = UUID.Zero; + if (UUID.TryParse(asset.ID, out uuid)) + { + asset.FullID = uuid; + } + if(asset.FullID == UUID.Zero) + { + m_log.WarnFormat("[Assets] Zero IDs: {0}",asset.Name); + asset.FullID = UUID.Random(); + asset.ID = asset.FullID.ToString(); + } + } + + if (m_Cache != null) + m_Cache.Cache(asset); + + if (asset.Temporary || asset.Local) + { + return asset.ID; + } + + string uri = MapServer(asset.FullID.ToString()) + "/assets/"; + + string newID = null; + try + { + newID = SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, 10000, m_Auth); + } + catch + { + newID = null; + } + + if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) + { + //The asset upload failed, try later + if(m_sendRetries != null) + { + lock(m_sendRetries) + { + if(m_Cache == null) + { + if (m_sendRetries[0] == null) + m_sendRetries[0] = new List(); + m_sendRetries[0].Add(asset); + } + else + { + if (m_sendCachedRetries[0] == null) + m_sendCachedRetries[0] = new List(); + m_sendCachedRetries[0].Add(asset.ID); + } + + m_log.WarnFormat("[Assets] Upload failed: {0} type {1} will retry later", + asset.ID.ToString(), asset.Type.ToString()); + m_retryTimer.Start(); + } + } + } + else + { + if (newID != asset.ID) + { + // Placing this here, so that this work with old asset servers that don't send any reply back + // SynchronousRestObjectRequester returns somethins that is not an empty string + asset.ID = newID; + if (m_Cache != null) + m_Cache.Cache(asset); + } + } + + return asset.ID; + } + + public virtual string StoreForeign(AssetBase asset) + { + int type = Util.ParseForeignAssetID(asset.ID, out string uri, out string uuidstr); + if(type < 0) + return string.Empty; + + if(type != 0) + asset.ID = uuidstr; + + if (m_Cache != null) + m_Cache.Cache(asset); + + if (asset.Temporary || asset.Local) + { + return asset.ID; + } + + IServiceAuth auth = null; + if(type == 0) + { + uri = MapServer(uuidstr) + "/assets/"; + auth = m_Auth; + } + else + uri += "/assets/"; + + string newID = null; + try + { + newID = SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, 30000, auth); + } + catch + { + newID = null; + } + + if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) + { + return string.Empty; + } + else + { + if (newID != asset.ID) + { + asset.ID = newID; + if (m_Cache != null) + m_Cache.Cache(asset); + } + } + + return asset.ID; + } + + public void retryStore(AssetBase asset, int nextRetryLevel) + { + string uri = MapServer(asset.FullID.ToString()) + "/assets/"; + + string newID = null; + try + { + newID = SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, 100000, m_Auth); + } + catch + { + newID = null; + } + + if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) + { + if (nextRetryLevel >= MAXSENDRETRIESLEN) + m_log.WarnFormat("[Assets] Giving up on uploading after {2} retries id: {0} type {1}", + asset.ID.ToString(), asset.Type.ToString(), MAXSENDRETRIESLEN); + else + { + lock (m_sendRetries) + { + if (m_sendRetries[nextRetryLevel] == null) + m_sendRetries[nextRetryLevel] = new List(); + + List m_queue = m_sendRetries[nextRetryLevel]; + m_queue.Add(asset); + m_log.WarnFormat("[Assets] Upload failed: {0} type {1} will retry later", + asset.ID.ToString(), asset.Type.ToString()); + } + } + } + else + { + m_log.InfoFormat("[Assets] Upload of {0} succeeded after {1} failed attempts", asset.ID.ToString(), nextRetryLevel.ToString()); + if (newID != asset.ID) + { + asset.ID = newID; + m_Cache?.Cache(asset); + } + } + } + + public void retryCachedStore(string assetID, int nextRetryLevel) + { + m_Cache.Get(assetID,out AssetBase asset); + if(asset == null) + { + m_log.WarnFormat("[Assets] asset not in cache on uploading after {2} retries id: {0}", + assetID, MAXSENDRETRIESLEN); + } + + string uri = MapServer(asset.FullID.ToString()) + "/assets/"; + + string newID = null; + try + { + newID = SynchronousRestObjectRequester. + MakeRequest("POST", uri, asset, 100000, m_Auth); + } + catch + { + newID = null; + } + + if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) + { + if (nextRetryLevel >= MAXSENDRETRIESLEN) + m_log.WarnFormat("[Assets] Giving up on uploading after {2} retries id: {0} type {1}", + asset.ID.ToString(), asset.Type.ToString(), MAXSENDRETRIESLEN); + else + { + lock (m_sendRetries) + { + if (m_sendCachedRetries[nextRetryLevel] == null) + m_sendCachedRetries[nextRetryLevel] = new List(); + + m_sendCachedRetries[nextRetryLevel].Add(assetID); + m_log.WarnFormat("[Assets] Upload failed: {0} type {1} will retry later", + asset.ID.ToString(), asset.Type.ToString()); + } + } + } + else + { + m_log.InfoFormat("[Assets] Upload of {0} succeeded after {1} failed attempts", asset.ID.ToString(), nextRetryLevel.ToString()); + if (newID != asset.ID) + { + asset.ID = newID; + m_Cache?.Cache(asset); + } + } + } + + public virtual bool UpdateContent(string id, byte[] data) + { + AssetBase asset = null; + + m_Cache?.Get(id, out asset); + + if (asset == null) + { + AssetMetadata metadata = GetMetadata(id); + if (metadata == null) + return false; + + asset = new AssetBase(metadata.FullID, metadata.Name, metadata.Type, UUID.Zero.ToString()); + asset.Metadata = metadata; + } + asset.Data = data; + + string uri = MapServer(id) + "/assets/" + id; + + if (SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, m_Auth)) + { + m_Cache?.Cache(asset, true); + return true; + } + return false; + } + + public virtual bool Delete(string id) + { + string uri = MapServer(id) + "/assets/" + id; + + if (SynchronousRestObjectRequester.MakeRequest("DELETE", uri, 0, m_Auth)) + { + if (m_Cache != null) + m_Cache.Expire(id); + + return true; + } + return false; + } + } +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RemoteAssetServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RemoteAssetServiceConnector.cs index 584c5cd9bf..03499a7524 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RemoteAssetServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/RemoteAssetServiceConnector.cs @@ -40,13 +40,11 @@ using OpenSim.Services.Interfaces; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "RemoteAssetServicesConnector")] - public class RemoteAssetServicesConnector : AssetServicesConnector, ISharedRegionModule, IAssetService + public class RemoteAssetServicesConnector : RegionBaseAssetServicesConnector, ISharedRegionModule, IAssetService { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private bool m_Enabled = false; - private IAssetCache m_Cache; - public Type ReplaceableInterface { get { return null; } @@ -57,7 +55,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset get { return "RemoteAssetServicesConnector"; } } - public override void Initialise(IConfigSource source) + public void Initialise(IConfigSource source) { IConfig moduleConfig = source.Configs["Modules"]; if (moduleConfig != null) @@ -65,17 +63,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset string name = moduleConfig.GetString("AssetServices", ""); if (name == Name) { - IConfig assetConfig = source.Configs["AssetService"]; - if (assetConfig == null) - { - m_log.Error("[ASSET CONNECTOR]: AssetService missing from OpenSim.ini"); - return; - } - + baseInitialise(source); m_Enabled = true; - base.Initialise(source); - m_log.Info("[ASSET CONNECTOR]: Remote assets enabled"); } } @@ -115,17 +105,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset // if (!(m_Cache is ISharedRegionModule)) m_Cache = null; - else - SetCache(m_Cache); - } - m_log.InfoFormat("[ASSET CONNECTOR]: Enabled remote assets for region {0}", scene.RegionInfo.RegionName); - if (m_Cache != null) - { - m_log.InfoFormat("[ASSET CONNECTOR]: Enabled asset caching for region {0}", scene.RegionInfo.RegionName); - } + m_log.InfoFormat("[ASSET CONNECTOR]: Enabled remote assets with caching for region {0}", scene.RegionInfo.RegionName); + else + m_log.InfoFormat("[ASSET CONNECTOR]: Enabled remote assets without caching for region {0}", scene.RegionInfo.RegionName); } } } diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index 3930a68ce0..f297068374 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -113,6 +113,11 @@ namespace OpenSim.Services.AssetService } } + public AssetBase Get(string id, string ForeignAssetService) + { + return null; + } + public virtual AssetBase GetCached(string id) { return Get(id); diff --git a/OpenSim/Services/AssetService/XAssetService.cs b/OpenSim/Services/AssetService/XAssetService.cs index 8106b70cc7..9db4d724e5 100644 --- a/OpenSim/Services/AssetService/XAssetService.cs +++ b/OpenSim/Services/AssetService/XAssetService.cs @@ -129,6 +129,11 @@ namespace OpenSim.Services.AssetService } } + public AssetBase Get(string id, string ForeignAssetService) + { + return null; + } + public virtual AssetBase GetCached(string id) { return Get(id); diff --git a/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs b/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs index 6709057c39..d2613b43c2 100644 --- a/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs +++ b/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs @@ -46,19 +46,10 @@ namespace OpenSim.Services.Connectors { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - const int MAXSENDRETRIESLEN = 30; + protected IAssetCache m_Cache = null; + public readonly object ConnectorLock = new object(); - public object ConnectorLock = new object(); - - private string m_ServerURI = String.Empty; - private IAssetCache m_Cache = null; - private int m_retryCounter; - private bool m_inRetries; - private List[] m_sendRetries = new List[MAXSENDRETRIESLEN]; - private List[] m_sendCachedRetries = new List[MAXSENDRETRIESLEN]; - private System.Timers.Timer m_retryTimer; - - private int m_maxAssetRequestConcurrency = 8; + private string m_ServerURI = string.Empty; private delegate void AssetRetrievedEx(AssetBase asset); @@ -67,16 +58,6 @@ namespace OpenSim.Services.Connectors private Dictionary> m_AssetHandlers = new Dictionary>(); - private Dictionary m_UriMap; - - private Thread[] m_fetchThreads; - - public int MaxAssetRequestConcurrency - { - get { return m_maxAssetRequestConcurrency; } - set { m_maxAssetRequestConcurrency = value; } - } - public AssetServicesConnector() { } @@ -94,8 +75,6 @@ namespace OpenSim.Services.Connectors public virtual void Initialise(IConfigSource source) { IConfig netconfig = source.Configs["Network"]; - if (netconfig != null) - m_maxAssetRequestConcurrency = netconfig.GetInt("MaxRequestConcurrency",m_maxAssetRequestConcurrency); IConfig assetConfig = source.Configs["AssetService"]; if (assetConfig == null) @@ -107,8 +86,8 @@ namespace OpenSim.Services.Connectors m_ServerURI = assetConfig.GetString("AssetServerURI", string.Empty); if (string.IsNullOrEmpty(m_ServerURI)) { - IConfig netConfig = source.Configs["Network"]; - m_ServerURI = netConfig.GetString("asset_server_url", string.Empty); + if(netconfig != null) + m_ServerURI = netconfig.GetString("asset_server_url", string.Empty); } if (string.IsNullOrEmpty(m_ServerURI)) { @@ -124,173 +103,17 @@ namespace OpenSim.Services.Connectors } m_ServerURI = m_GridAssetsURL.URI; - bool usemaps = assetConfig.GetBoolean("AssetServerIsCluster", false); - - if(usemaps) - { - m_UriMap = new Dictionary(); - for (int i = 0; i < 256; i++) - { - string prefix = i.ToString("x2"); - string groupHost = assetConfig.GetString("AssetServerHost_" + prefix, string.Empty); - if(string.IsNullOrEmpty(groupHost)) - m_UriMap[prefix] = m_ServerURI; - else - { - OSHHTPHost other = new OSHHTPHost(groupHost, true); - if(!other.IsResolvedHost) - { - m_log.Error("[ASSET CONNECTOR]: Could not parse or resolve AssetServerHost_" + prefix); - throw new Exception("Asset connector init error"); - } - m_UriMap[prefix] = other.URI; - } - } - } - else - m_UriMap = null; - - m_retryTimer = new System.Timers.Timer(); - m_retryTimer.Elapsed += new ElapsedEventHandler(retryCheck); - m_retryTimer.AutoReset = true; - m_retryTimer.Interval = 60000; - - m_fetchThreads = new Thread[3]; - Initialise(source, "AssetService"); - - for (int i = 0 ; i < m_fetchThreads.Length; i++) - { - m_fetchThreads[i] = WorkManager.StartThread(AssetRequestProcessor, string.Format("GetAssetsWorker{0}", i)); - } } - private string MapServer(string id) + private int m_maxAssetRequestConcurrency = 8; + public int MaxAssetRequestConcurrency { - if (m_UriMap == null) - return m_ServerURI; - - string prefix = id.Substring(0, 2).ToLower(); - - if (m_UriMap.TryGetValue(prefix, out string host)) - return host; - - return m_UriMap["00"]; + get { return m_maxAssetRequestConcurrency; } + set { m_maxAssetRequestConcurrency = value; } } - protected void retryCheck(object source, ElapsedEventArgs e) - { - lock(m_sendRetries) - { - if(m_inRetries) - return; - m_inRetries = true; - } - - m_retryCounter++; - if(m_retryCounter >= 61 ) // avoid overflow 60 is max in use below - m_retryCounter = 1; - - int inUse = 0; - int nextlevel; - int timefactor; - if(m_Cache == null) - { - List retrylist; - // we need to go down - for(int i = MAXSENDRETRIESLEN - 1; i >= 0; i--) - { - lock(m_sendRetries) - retrylist = m_sendRetries[i]; - - if(retrylist == null) - continue; - - inUse++; - nextlevel = i + 1; - - //We exponentially fall back on frequency until we reach one attempt per hour - //The net result is that we end up in the queue for roughly 24 hours.. - //24 hours worth of assets could be a lot, so the hope is that the region admin - //will have gotten the asset connector back online quickly! - if(i == 0) - timefactor = 1; - else - { - timefactor = 1 << nextlevel; - if (timefactor > 60) - timefactor = 60; - } - - if(m_retryCounter < timefactor) - continue; // to update inUse; - - if (m_retryCounter % timefactor != 0) - continue; - - // a list to retry - lock(m_sendRetries) - m_sendRetries[i] = null; - - // we are the only ones with a copy of this retrylist now - foreach(AssetBase ass in retrylist) - retryStore(ass, nextlevel); - } - } - else - { - List retrylist; - // we need to go down - for (int i = MAXSENDRETRIESLEN - 1; i >= 0; i--) - { - lock (m_sendRetries) - retrylist = m_sendCachedRetries[i]; - - if (retrylist == null) - continue; - - inUse++; - nextlevel = i + 1; - - //We exponentially fall back on frequency until we reach one attempt per hour - //The net result is that we end up in the queue for roughly 24 hours.. - //24 hours worth of assets could be a lot, so the hope is that the region admin - //will have gotten the asset connector back online quickly! - if (i == 0) - timefactor = 1; - else - { - timefactor = 1 << nextlevel; - if (timefactor > 60) - timefactor = 60; - } - - if (m_retryCounter < timefactor) - continue; // to update inUse; - - if (m_retryCounter % timefactor != 0) - continue; - - // a list to retry - lock (m_sendRetries) - m_sendCachedRetries[i] = null; - - // we are the only ones with a copy of this retrylist now - foreach (string id in retrylist) - retryCachedStore(id, nextlevel); - } - } - - lock (m_sendRetries) - { - if(inUse == 0 ) - m_retryTimer.Stop(); - - m_inRetries = false; - } - } - - public void SetCache(IAssetCache cache) + protected void SetCache(IAssetCache cache) { m_Cache = cache; } @@ -317,7 +140,7 @@ namespace OpenSim.Services.Connectors if (asset == null || asset.Data == null || asset.Data.Length == 0) { - string uri = MapServer(id) + "/assets/" + id; + string uri = m_ServerURI + "/assets/" + id; asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, m_Auth); if (m_Cache != null) @@ -331,34 +154,9 @@ namespace OpenSim.Services.Connectors return asset; } - public AssetBase GetForeign(string id) + public AssetBase Get(string id, string ForeignAssetService) { - int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); - if (type < 0) - return null; - - AssetBase asset = null; - if (m_Cache != null) - { - //if (!m_Cache.Get(uuidstr, out asset)) - // return null; - m_Cache.Get(uuidstr, out asset); // negative cache is a fail on HG - } - - if (asset == null || asset.Data == null || asset.Data.Length == 0) - { - IServiceAuth auth = null; - if (type == 0) - { - uri = MapServer(uuidstr) + "/assets/" + uuidstr; - auth = m_Auth; - } - else - uri = uri + "/assets/" + uuidstr; - - asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, auth); - } - return asset; + return null; } public virtual AssetMetadata GetMetadata(string id) @@ -373,40 +171,12 @@ namespace OpenSim.Services.Connectors return fullAsset.Metadata; } - string uri = MapServer(id) + "/assets/" + id + "/metadata"; + string uri =m_ServerURI + "/assets/" + id + "/metadata"; AssetMetadata asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, m_Auth); return asset; } - public AssetMetadata GetForeignMetadata(string id) - { - int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); - if (type < 0) - return null; - - if (m_Cache != null) - { - AssetBase fullAsset; - if (!m_Cache.Get(uuidstr, out fullAsset)) - return null; - - if (fullAsset != null) - return fullAsset.Metadata; - } - - IServiceAuth auth = null; - if (type == 0) - { - auth = m_Auth; - uri = MapServer(uuidstr) + "/assets/" + uuidstr + "/metadata"; - } - else - uri = uri + "/assets/" + uuidstr + "/metadata"; - - AssetMetadata asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, auth); - return asset; - } public virtual byte[] GetData(string id) { @@ -419,9 +189,7 @@ namespace OpenSim.Services.Connectors return fullAsset.Data; } - string uri = MapServer(id); - - using (RestClient rc = new RestClient(uri)) + using (RestClient rc = new RestClient(m_ServerURI)) { rc.AddResourcePath("assets/" + id + "/Data"); rc.RequestMethod = "GET"; @@ -435,49 +203,6 @@ namespace OpenSim.Services.Connectors } } - public byte[] GetForeignData(string id) - { - int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); - if (type < 0) - return null; - - if (m_Cache != null) - { - if (!m_Cache.Get(uuidstr, out AssetBase fullAsset)) - return null; - - if (fullAsset != null) - return fullAsset.Data; - } - - IServiceAuth auth = null; - if (type == 0) - { - uri = MapServer(uuidstr); - auth = m_Auth; - } - - using (RestClient rc = new RestClient(uri)) - { - rc.AddResourcePath("assets/" + id + "/Data"); - rc.RequestMethod = "GET"; - - using (MemoryStream s = rc.Request(auth)) - { - if (s == null || s.Length == 0) - return null; - return s.ToArray(); - } - } - } - - private class QueuedAssetRequest - { - public string uri; - public string id; - public IServiceAuth auth; - } - public virtual bool Get(string id, object sender, AssetRetrieved handler) { AssetBase asset = null; @@ -489,7 +214,7 @@ namespace OpenSim.Services.Connectors if (asset == null) { - string uri = MapServer(id) + "/assets/" + id; + string uri = m_ServerURI + "/assets/" + id; lock (m_AssetHandlers) { @@ -511,8 +236,10 @@ namespace OpenSim.Services.Connectors QueuedAssetRequest request = new QueuedAssetRequest(); request.id = id; request.uri = uri; - request.auth = m_Auth; - m_requestQueue.Add(request); + Util.FireAndForget(x => + { + AssetRequestProcessor(request); + }); } } else @@ -525,108 +252,40 @@ namespace OpenSim.Services.Connectors return true; } - public bool GetForeign(string id, object sender, AssetRetrieved handler) + private class QueuedAssetRequest { - int type = Util.ParseForeignAssetID(id, out string uri, out string uuidstr); - if (type < 0) - return false; + public string uri; + public string id; + } - AssetBase asset = null; - if (m_Cache != null) + private void AssetRequestProcessor(QueuedAssetRequest r) + { + string id = r.id; + try { - m_Cache.Get(uuidstr, out asset); - } + AssetBase a = SynchronousRestObjectRequester.MakeRequest("GET", r.uri, 0, 30000, m_Auth); - if (asset == null) - { - IServiceAuth auth = null; - if (type == 0) - { - uri = MapServer(uuidstr) + "/assets/" + uuidstr; - auth = m_Auth; - } - else - uri = uri + "/assets/" + uuidstr; + if (a != null && m_Cache != null) + m_Cache.Cache(a); + List handlers; lock (m_AssetHandlers) { - AssetRetrievedEx handlerEx = new AssetRetrievedEx(delegate (AssetBase _asset) { handler(id, sender, _asset); }); - - List handlers; - if (m_AssetHandlers.TryGetValue(id, out handlers)) - { - // Someone else is already loading this asset. It will notify our handler when done. - handlers.Add(handlerEx); - return true; - } - - handlers = new List(); - handlers.Add(handlerEx); - - m_AssetHandlers.Add(id, handlers); - - QueuedAssetRequest request = new QueuedAssetRequest(); - request.id = id; - request.uri = uri; - request.auth = m_Auth; - m_requestQueue.Add(request); + handlers = m_AssetHandlers[id]; + m_AssetHandlers.Remove(id); } - } - else - { - if (asset != null && (asset.Data == null || asset.Data.Length == 0)) - asset = null; - handler(id, sender, asset); - } - return true; - } - - private BlockingCollection m_requestQueue = new BlockingCollection(); - private void AssetRequestProcessor() - { - QueuedAssetRequest r; - - while (true) - { - if(!m_requestQueue.TryTake(out r, 4500) || r == null) + if(handlers != null) { - Watchdog.UpdateThread(); - continue; - } - - Watchdog.UpdateThread(); - string id = r.id; - - try - { - AssetBase a = SynchronousRestObjectRequester.MakeRequest("GET", r.uri, 0, 30000, r.auth); - - if (a != null && m_Cache != null) - m_Cache.Cache(a); - - List handlers; - lock (m_AssetHandlers) + foreach (AssetRetrievedEx h in handlers) { - handlers = m_AssetHandlers[id]; - m_AssetHandlers.Remove(id); - } - - if(handlers != null) - { - Util.FireAndForget(x => - { - foreach (AssetRetrievedEx h in handlers) - { - try { h.Invoke(a); } - catch { } - } - handlers.Clear(); - }); + try { h.Invoke(a); } + catch { } } + handlers.Clear(); } - catch { } } + catch { } } public virtual bool[] AssetsExist(string[] ids) @@ -650,85 +309,6 @@ namespace OpenSim.Services.Connectors return exist; } - private struct AssetAndIndex - { - public string assetID; - public int index; - public IServiceAuth auth; - - public AssetAndIndex(string assetID, int index, IServiceAuth auth) - { - this.assetID = assetID; - this.index = index; - this.auth = auth; - } - } - - public bool[] ForeignAssetsExist(string[] ids) - { - bool[] exist = new bool[ids.Length]; - - var url2assets = new Dictionary>(); - - for (int i = 0; i < ids.Length; i++) - { - int ltype = Util.ParseForeignAssetID(ids[i], out string lurl, out string luuidstr); - if (ltype > 0) - { - IServiceAuth auth = null; - - if (ltype == 0) - { - lurl = m_ServerURI; - auth = m_Auth; - } - - List < AssetAndIndex > lst; - if (!url2assets.TryGetValue(lurl, out lst)) - { - lst = new List(); - url2assets.Add(lurl, lst); - } - lst.Add(new AssetAndIndex(luuidstr, i, auth)); - } - } - - // Query each of the servers in turn - foreach (KeyValuePair> kvp in url2assets) - { - List curAssets = kvp.Value; - string[] assetIDs = new string[curAssets.Count]; - IServiceAuth auth = curAssets[0].auth; - for (int i = 0; i < assetIDs.Length;++i) - assetIDs[i] = curAssets[i].assetID; - - string uri = kvp.Key + "/get_assets_exist"; - - bool[] curExist = null; - try - { - curExist = SynchronousRestObjectRequester.MakeRequest("POST", uri, assetIDs, auth); - } - catch (Exception) - { - // This is most likely to happen because the server doesn't support this function, - // so just silently return "doesn't exist" for all the assets. - } - - if(curExist != null) - { - int i = 0; - foreach (AssetAndIndex ai in curAssets) - { - exist[ai.index] = curExist[i]; - ++i; - } - } - } - - return exist; - } - string stringUUIDZero = UUID.Zero.ToString(); public virtual string Store(AssetBase asset) @@ -770,7 +350,7 @@ namespace OpenSim.Services.Connectors return asset.ID; } - string uri = MapServer(asset.FullID.ToString()) + "/assets/"; + string uri = m_ServerURI + "/assets/"; string newID = null; try @@ -784,26 +364,7 @@ namespace OpenSim.Services.Connectors if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) { - //The asset upload failed, try later - lock(m_sendRetries) - { - if(m_Cache == null) - { - if (m_sendRetries[0] == null) - m_sendRetries[0] = new List(); - m_sendRetries[0].Add(asset); - } - else - { - if (m_sendCachedRetries[0] == null) - m_sendCachedRetries[0] = new List(); - m_sendCachedRetries[0].Add(asset.ID); - } - - m_log.WarnFormat("[Assets] Upload failed: {0} type {1} will retry later", - asset.ID.ToString(), asset.Type.ToString()); - m_retryTimer.Start(); - } + return string.Empty; } else { @@ -820,154 +381,6 @@ namespace OpenSim.Services.Connectors return asset.ID; } - public virtual string StoreForeign(AssetBase asset) - { - int type = Util.ParseForeignAssetID(asset.ID, out string uri, out string uuidstr); - if(type < 0) - return string.Empty; - - if(type != 0) - asset.ID = uuidstr; - - if (m_Cache != null) - m_Cache.Cache(asset); - - if (asset.Temporary || asset.Local) - { - return asset.ID; - } - - IServiceAuth auth = null; - if(type == 0) - { - uri = MapServer(uuidstr) + "/assets/"; - auth = m_Auth; - } - else - uri += "/assets/"; - - string newID = null; - try - { - newID = SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, 30000, auth); - } - catch - { - newID = null; - } - - if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) - { - return string.Empty; - } - else - { - if (newID != asset.ID) - { - asset.ID = newID; - if (m_Cache != null) - m_Cache.Cache(asset); - } - } - - return asset.ID; - } - - public void retryStore(AssetBase asset, int nextRetryLevel) - { - string uri = MapServer(asset.FullID.ToString()) + "/assets/"; - - string newID = null; - try - { - newID = SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, 100000, m_Auth); - } - catch - { - newID = null; - } - - if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) - { - if (nextRetryLevel >= MAXSENDRETRIESLEN) - m_log.WarnFormat("[Assets] Giving up on uploading after {2} retries id: {0} type {1}", - asset.ID.ToString(), asset.Type.ToString(), MAXSENDRETRIESLEN); - else - { - lock (m_sendRetries) - { - if (m_sendRetries[nextRetryLevel] == null) - m_sendRetries[nextRetryLevel] = new List(); - - List m_queue = m_sendRetries[nextRetryLevel]; - m_queue.Add(asset); - m_log.WarnFormat("[Assets] Upload failed: {0} type {1} will retry later", - asset.ID.ToString(), asset.Type.ToString()); - } - } - } - else - { - m_log.InfoFormat("[Assets] Upload of {0} succeeded after {1} failed attempts", asset.ID.ToString(), nextRetryLevel.ToString()); - if (newID != asset.ID) - { - asset.ID = newID; - m_Cache?.Cache(asset); - } - } - } - - public void retryCachedStore(string assetID, int nextRetryLevel) - { - m_Cache.Get(assetID,out AssetBase asset); - if(asset == null) - { - m_log.WarnFormat("[Assets] asset not in cache on uploading after {2} retries id: {0}", - assetID, MAXSENDRETRIESLEN); - } - - string uri = MapServer(asset.FullID.ToString()) + "/assets/"; - - string newID = null; - try - { - newID = SynchronousRestObjectRequester. - MakeRequest("POST", uri, asset, 100000, m_Auth); - } - catch - { - newID = null; - } - - if (string.IsNullOrEmpty(newID) || newID == stringUUIDZero) - { - if (nextRetryLevel >= MAXSENDRETRIESLEN) - m_log.WarnFormat("[Assets] Giving up on uploading after {2} retries id: {0} type {1}", - asset.ID.ToString(), asset.Type.ToString(), MAXSENDRETRIESLEN); - else - { - lock (m_sendRetries) - { - if (m_sendCachedRetries[nextRetryLevel] == null) - m_sendCachedRetries[nextRetryLevel] = new List(); - - m_sendCachedRetries[nextRetryLevel].Add(assetID); - m_log.WarnFormat("[Assets] Upload failed: {0} type {1} will retry later", - asset.ID.ToString(), asset.Type.ToString()); - } - } - } - else - { - m_log.InfoFormat("[Assets] Upload of {0} succeeded after {1} failed attempts", asset.ID.ToString(), nextRetryLevel.ToString()); - if (newID != asset.ID) - { - asset.ID = newID; - m_Cache?.Cache(asset); - } - } - } - public virtual bool UpdateContent(string id, byte[] data) { AssetBase asset = null; @@ -985,7 +398,7 @@ namespace OpenSim.Services.Connectors } asset.Data = data; - string uri = MapServer(id) + "/assets/" + id; + string uri = m_ServerURI + "/assets/" + id; if (SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, m_Auth)) { @@ -997,7 +410,7 @@ namespace OpenSim.Services.Connectors public virtual bool Delete(string id) { - string uri = MapServer(id) + "/assets/" + id; + string uri = m_ServerURI + "/assets/" + id; if (SynchronousRestObjectRequester.MakeRequest("DELETE", uri, 0, m_Auth)) { diff --git a/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs b/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs index e63401c4a3..c8bfdb1bf2 100644 --- a/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs +++ b/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs @@ -92,6 +92,12 @@ namespace OpenSim.Services.Connectors return null; } + public AssetBase Get(string id, string ForeignAssetService) + { + IAssetService connector = GetConnector(ForeignAssetService); + return connector.Get(id); + } + public AssetBase GetCached(string id) { string url = string.Empty; diff --git a/OpenSim/Services/FSAssetService/FSAssetService.cs b/OpenSim/Services/FSAssetService/FSAssetService.cs index 2fb3e6ce90..5bb4447c07 100644 --- a/OpenSim/Services/FSAssetService/FSAssetService.cs +++ b/OpenSim/Services/FSAssetService/FSAssetService.cs @@ -420,6 +420,11 @@ namespace OpenSim.Services.FSAssetService return Get(id, out hash); } + public AssetBase Get(string id, string ForeignAssetService) + { + return null; + } + private AssetBase Get(string id, out string sha) { string hash = string.Empty; diff --git a/OpenSim/Services/HypergridService/HGAssetService.cs b/OpenSim/Services/HypergridService/HGAssetService.cs index a54ec65739..2056be5a41 100755 --- a/OpenSim/Services/HypergridService/HGAssetService.cs +++ b/OpenSim/Services/HypergridService/HGAssetService.cs @@ -125,6 +125,11 @@ namespace OpenSim.Services.HypergridService return asset; } + public AssetBase Get(string id, string ForeignAssetService) + { + return null; + } + public AssetMetadata GetMetadata(string id) { AssetMetadata meta = m_assetService.GetMetadata(id); diff --git a/OpenSim/Services/HypergridService/HGRemoteAssetService.cs b/OpenSim/Services/HypergridService/HGRemoteAssetService.cs index 5e98af9ed9..151d50dd85 100644 --- a/OpenSim/Services/HypergridService/HGRemoteAssetService.cs +++ b/OpenSim/Services/HypergridService/HGRemoteAssetService.cs @@ -117,6 +117,11 @@ namespace OpenSim.Services.HypergridService return asset; } + public AssetBase Get(string id, string ForeignAssetService) + { + return null; + } + public AssetMetadata GetMetadata(string id) { AssetMetadata meta = m_assetConnector.GetMetadata(id); diff --git a/OpenSim/Services/Interfaces/IAssetService.cs b/OpenSim/Services/Interfaces/IAssetService.cs index 170dc97cce..3c456e13aa 100644 --- a/OpenSim/Services/Interfaces/IAssetService.cs +++ b/OpenSim/Services/Interfaces/IAssetService.cs @@ -40,6 +40,7 @@ namespace OpenSim.Services.Interfaces /// /// AssetBase Get(string id); + AssetBase Get(string id, string ForeignAssetService); /// /// Get an asset's metadata