OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
AssetsRequest.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Threading;
32 using System.Timers;
33 using log4net;
34 using OpenMetaverse;
35 using OpenSim.Framework;
36 using OpenSim.Framework.Monitoring;
37 using OpenSim.Framework.Serialization;
38 using OpenSim.Framework.Serialization.External;
39 using OpenSim.Services.Interfaces;
40 
41 namespace OpenSim.Region.CoreModules.World.Archiver
42 {
47  {
48  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49 
53  public delegate void AssetsRequestCallback(
54  ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids, bool timedOut);
55 
56  enum RequestState
57  {
58  Initial,
59  Running,
60  Completed,
61  Aborted
62  };
63 
68  protected const int TIMEOUT = 60 * 1000;
69 
73  protected const int MAX_UUID_DISPLAY_ON_TIMEOUT = 3;
74 
75  protected System.Timers.Timer m_requestCallbackTimer;
76 
80  private RequestState m_requestState = RequestState.Initial;
81 
85  protected IDictionary<UUID, sbyte> m_uuids;
86 
90  protected AssetsRequestCallback m_assetsRequestCallback;
91 
95  protected List<UUID> m_foundAssetUuids = new List<UUID>();
96 
100  protected List<UUID> m_notFoundAssetUuids = new List<UUID>();
101 
105  private int m_repliesRequired;
106 
112  protected UUID m_scopeID; // the grid ID
113 
115 
116  protected Dictionary<string, object> m_options;
117 
118  protected internal AssetsRequest(
119  AssetsArchiver assetsArchiver, IDictionary<UUID, sbyte> uuids,
120  IAssetService assetService, IUserAccountService userService,
121  UUID scope, Dictionary<string, object> options,
122  AssetsRequestCallback assetsRequestCallback)
123  {
124  m_assetsArchiver = assetsArchiver;
125  m_uuids = uuids;
126  m_assetsRequestCallback = assetsRequestCallback;
127  m_assetService = assetService;
128  m_userAccountService = userService;
129  m_scopeID = scope;
130  m_options = options;
131  m_repliesRequired = uuids.Count;
132 
133  // FIXME: This is a really poor way of handling the timeout since it will always leave the original requesting thread
134  // hanging. Need to restructure so an original request thread waits for a ManualResetEvent on asset received
135  // so we can properly abort that thread. Or request all assets synchronously, though that would be a more
136  // radical change
137  m_requestCallbackTimer = new System.Timers.Timer(TIMEOUT);
138  m_requestCallbackTimer.AutoReset = false;
139  m_requestCallbackTimer.Elapsed += new ElapsedEventHandler(OnRequestCallbackTimeout);
140  }
141 
142  protected internal void Execute()
143  {
144  m_requestState = RequestState.Running;
145 
146  m_log.DebugFormat("[ARCHIVER]: AssetsRequest executed looking for {0} possible assets", m_repliesRequired);
147 
148  // We can stop here if there are no assets to fetch
149  if (m_repliesRequired == 0)
150  {
151  m_requestState = RequestState.Completed;
152  PerformAssetsRequestCallback(false);
153  return;
154  }
155 
156  m_requestCallbackTimer.Enabled = true;
157 
158  foreach (KeyValuePair<UUID, sbyte> kvp in m_uuids)
159  {
160 // m_log.DebugFormat("[ARCHIVER]: Requesting asset {0}", kvp.Key);
161 
162 // m_assetService.Get(kvp.Key.ToString(), kvp.Value, PreAssetRequestCallback);
163  AssetBase asset = m_assetService.Get(kvp.Key.ToString());
164  PreAssetRequestCallback(kvp.Key.ToString(), kvp.Value, asset);
165  }
166  }
167 
168  protected void OnRequestCallbackTimeout(object source, ElapsedEventArgs args)
169  {
170  bool timedOut = true;
171 
172  try
173  {
174  lock (this)
175  {
176  // Take care of the possibilty that this thread started but was paused just outside the lock before
177  // the final request came in (assuming that such a thing is possible)
178  if (m_requestState == RequestState.Completed)
179  {
180  timedOut = false;
181  return;
182  }
183 
184  m_requestState = RequestState.Aborted;
185  }
186 
187  // Calculate which uuids were not found. This is an expensive way of doing it, but this is a failure
188  // case anyway.
189  List<UUID> uuids = new List<UUID>();
190  foreach (UUID uuid in m_uuids.Keys)
191  {
192  uuids.Add(uuid);
193  }
194 
195  foreach (UUID uuid in m_foundAssetUuids)
196  {
197  uuids.Remove(uuid);
198  }
199 
200  foreach (UUID uuid in m_notFoundAssetUuids)
201  {
202  uuids.Remove(uuid);
203  }
204 
205  m_log.ErrorFormat(
206  "[ARCHIVER]: Asset service failed to return information about {0} requested assets", uuids.Count);
207 
208  int i = 0;
209  foreach (UUID uuid in uuids)
210  {
211  m_log.ErrorFormat("[ARCHIVER]: No information about asset {0} received", uuid);
212 
213  if (++i >= MAX_UUID_DISPLAY_ON_TIMEOUT)
214  break;
215  }
216 
217  if (uuids.Count > MAX_UUID_DISPLAY_ON_TIMEOUT)
218  m_log.ErrorFormat(
219  "[ARCHIVER]: (... {0} more not shown)", uuids.Count - MAX_UUID_DISPLAY_ON_TIMEOUT);
220 
221  m_log.Error("[ARCHIVER]: Archive save aborted. PLEASE DO NOT USE THIS ARCHIVE, IT WILL BE INCOMPLETE.");
222  }
223  catch (Exception e)
224  {
225  m_log.ErrorFormat("[ARCHIVER]: Timeout handler exception {0}{1}", e.Message, e.StackTrace);
226  }
227  finally
228  {
229  if (timedOut)
230  WorkManager.RunInThread(PerformAssetsRequestCallback, true, "Archive Assets Request Callback");
231  }
232  }
233 
234  protected void PreAssetRequestCallback(string fetchedAssetID, object assetType, AssetBase fetchedAsset)
235  {
236  // Check for broken asset types and fix them with the AssetType gleaned by UuidGatherer
237  if (fetchedAsset != null && fetchedAsset.Type == (sbyte)AssetType.Unknown)
238  {
239  m_log.InfoFormat("[ARCHIVER]: Rewriting broken asset type for {0} to {1}", fetchedAsset.ID, SLUtil.AssetTypeFromCode((sbyte)assetType));
240  fetchedAsset.Type = (sbyte)assetType;
241  }
242 
243  AssetRequestCallback(fetchedAssetID, this, fetchedAsset);
244  }
245 
251  public void AssetRequestCallback(string id, object sender, AssetBase asset)
252  {
253  Culture.SetCurrentCulture();
254 
255  try
256  {
257  lock (this)
258  {
259  //m_log.DebugFormat("[ARCHIVER]: Received callback for asset {0}", id);
260 
261  m_requestCallbackTimer.Stop();
262 
263  if ((m_requestState == RequestState.Aborted) || (m_requestState == RequestState.Completed))
264  {
265  m_log.WarnFormat(
266  "[ARCHIVER]: Received information about asset {0} while in state {1}. Ignoring.",
267  id, m_requestState);
268 
269  return;
270  }
271 
272  if (asset != null)
273  {
274 // m_log.DebugFormat("[ARCHIVER]: Writing asset {0}", id);
275  m_foundAssetUuids.Add(asset.FullID);
276 
277  m_assetsArchiver.WriteAsset(PostProcess(asset));
278  }
279  else
280  {
281 // m_log.DebugFormat("[ARCHIVER]: Recording asset {0} as not found", id);
282  m_notFoundAssetUuids.Add(new UUID(id));
283  }
284 
285  if (m_foundAssetUuids.Count + m_notFoundAssetUuids.Count >= m_repliesRequired)
286  {
287  m_requestState = RequestState.Completed;
288 
289  m_log.DebugFormat(
290  "[ARCHIVER]: Successfully added {0} assets ({1} assets not found but these may be expected invalid references)",
291  m_foundAssetUuids.Count, m_notFoundAssetUuids.Count);
292 
293  // We want to stop using the asset cache thread asap
294  // as we now need to do the work of producing the rest of the archive
295  WorkManager.RunInThread(PerformAssetsRequestCallback, false, "Archive Assets Request Callback");
296  }
297  else
298  {
299  m_requestCallbackTimer.Start();
300  }
301  }
302  }
303  catch (Exception e)
304  {
305  m_log.ErrorFormat("[ARCHIVER]: AssetRequestCallback failed with {0}", e);
306  }
307  }
308 
312  protected void PerformAssetsRequestCallback(object o)
313  {
314  Culture.SetCurrentCulture();
315 
316  Boolean timedOut = (Boolean)o;
317 
318  try
319  {
320  m_assetsRequestCallback(m_foundAssetUuids, m_notFoundAssetUuids, timedOut);
321  }
322  catch (Exception e)
323  {
324  m_log.ErrorFormat(
325  "[ARCHIVER]: Terminating archive creation since asset requster callback failed with {0}", e);
326  }
327  }
328 
329  protected AssetBase PostProcess(AssetBase asset)
330  {
331  if (asset.Type == (sbyte)AssetType.Object && asset.Data != null && m_options.ContainsKey("home"))
332  {
333  //m_log.DebugFormat("[ARCHIVER]: Rewriting object data for {0}", asset.ID);
334  string xml = ExternalRepresentationUtils.RewriteSOP(Utils.BytesToString(asset.Data), string.Empty, m_options["home"].ToString(), m_userAccountService, m_scopeID);
335  asset.Data = Utils.StringToBytes(xml);
336  }
337  return asset;
338  }
339  }
340 }
void PerformAssetsRequestCallback(object o)
Perform the callback on the original requester of the assets
sbyte Type
(sbyte) AssetType enum
Definition: AssetBase.cs:198
Asset class. All Assets are reference by this class or a class derived from this class ...
Definition: AssetBase.cs:49
Encapsulate the asynchronous requests for the assets required for an archive operation ...
void PreAssetRequestCallback(string fetchedAssetID, object assetType, AssetBase fetchedAsset)
void OnRequestCallbackTimeout(object source, ElapsedEventArgs args)
void AssetRequestCallback(string id, object sender, AssetBase asset)
Called back by the asset cache when it has the asset