opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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.Serialization;
37 using OpenSim.Framework.Serialization.External;
38 using OpenSim.Services.Interfaces;
39  
40 namespace OpenSim.Region.CoreModules.World.Archiver
41 {
42 /// <summary>
43 /// Encapsulate the asynchronous requests for the assets required for an archive operation
44 /// </summary>
45 class AssetsRequest
46 {
47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48  
49 /// <summary>
50 /// Method called when all the necessary assets for an archive request have been received.
51 /// </summary>
52 public delegate void AssetsRequestCallback(
53 ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids, bool timedOut);
54  
55 enum RequestState
56 {
57 Initial,
58 Running,
59 Completed,
60 Aborted
61 };
62  
63 /// <value>
64 /// Timeout threshold if we still need assets or missing asset notifications but have stopped receiving them
65 /// from the asset service
66 /// </value>
67 protected const int TIMEOUT = 60 * 1000;
68  
69 /// <value>
70 /// If a timeout does occur, limit the amount of UUID information put to the console.
71 /// </value>
72 protected const int MAX_UUID_DISPLAY_ON_TIMEOUT = 3;
73  
74 protected System.Timers.Timer m_requestCallbackTimer;
75  
76 /// <value>
77 /// State of this request
78 /// </value>
79 private RequestState m_requestState = RequestState.Initial;
80  
81 /// <value>
82 /// uuids to request
83 /// </value>
84 protected IDictionary<UUID, sbyte> m_uuids;
85  
86 /// <value>
87 /// Callback used when all the assets requested have been received.
88 /// </value>
89 protected AssetsRequestCallback m_assetsRequestCallback;
90  
91 /// <value>
92 /// List of assets that were found. This will be passed back to the requester.
93 /// </value>
94 protected List<UUID> m_foundAssetUuids = new List<UUID>();
95  
96 /// <value>
97 /// Maintain a list of assets that could not be found. This will be passed back to the requester.
98 /// </value>
99 protected List<UUID> m_notFoundAssetUuids = new List<UUID>();
100  
101 /// <value>
102 /// Record the number of asset replies required so we know when we've finished
103 /// </value>
104 private int m_repliesRequired;
105  
106 /// <value>
107 /// Asset service used to request the assets
108 /// </value>
109 protected IAssetService m_assetService;
110 protected IUserAccountService m_userAccountService;
111 protected UUID m_scopeID; // the grid ID
112  
113 protected AssetsArchiver m_assetsArchiver;
114  
115 protected Dictionary<string, object> m_options;
116  
117 protected internal AssetsRequest(
118 AssetsArchiver assetsArchiver, IDictionary<UUID, sbyte> uuids,
119 IAssetService assetService, IUserAccountService userService,
120 UUID scope, Dictionary<string, object> options,
121 AssetsRequestCallback assetsRequestCallback)
122 {
123 m_assetsArchiver = assetsArchiver;
124 m_uuids = uuids;
125 m_assetsRequestCallback = assetsRequestCallback;
126 m_assetService = assetService;
127 m_userAccountService = userService;
128 m_scopeID = scope;
129 m_options = options;
130 m_repliesRequired = uuids.Count;
131  
132 // FIXME: This is a really poor way of handling the timeout since it will always leave the original requesting thread
133 // hanging. Need to restructure so an original request thread waits for a ManualResetEvent on asset received
134 // so we can properly abort that thread. Or request all assets synchronously, though that would be a more
135 // radical change
136 m_requestCallbackTimer = new System.Timers.Timer(TIMEOUT);
137 m_requestCallbackTimer.AutoReset = false;
138 m_requestCallbackTimer.Elapsed += new ElapsedEventHandler(OnRequestCallbackTimeout);
139 }
140  
141 protected internal void Execute()
142 {
143 m_requestState = RequestState.Running;
144  
145 m_log.DebugFormat("[ARCHIVER]: AssetsRequest executed looking for {0} possible assets", m_repliesRequired);
146  
147 // We can stop here if there are no assets to fetch
148 if (m_repliesRequired == 0)
149 {
150 m_requestState = RequestState.Completed;
151 PerformAssetsRequestCallback(false);
152 return;
153 }
154  
155 m_requestCallbackTimer.Enabled = true;
156  
157 foreach (KeyValuePair<UUID, sbyte> kvp in m_uuids)
158 {
159 // m_log.DebugFormat("[ARCHIVER]: Requesting asset {0}", kvp.Key);
160  
161 // m_assetService.Get(kvp.Key.ToString(), kvp.Value, PreAssetRequestCallback);
162 AssetBase asset = m_assetService.Get(kvp.Key.ToString());
163 PreAssetRequestCallback(kvp.Key.ToString(), kvp.Value, asset);
164 }
165 }
166  
167 protected void OnRequestCallbackTimeout(object source, ElapsedEventArgs args)
168 {
169 bool timedOut = true;
170  
171 try
172 {
173 lock (this)
174 {
175 // Take care of the possibilty that this thread started but was paused just outside the lock before
176 // the final request came in (assuming that such a thing is possible)
177 if (m_requestState == RequestState.Completed)
178 {
179 timedOut = false;
180 return;
181 }
182  
183 m_requestState = RequestState.Aborted;
184 }
185  
186 // Calculate which uuids were not found. This is an expensive way of doing it, but this is a failure
187 // case anyway.
188 List<UUID> uuids = new List<UUID>();
189 foreach (UUID uuid in m_uuids.Keys)
190 {
191 uuids.Add(uuid);
192 }
193  
194 foreach (UUID uuid in m_foundAssetUuids)
195 {
196 uuids.Remove(uuid);
197 }
198  
199 foreach (UUID uuid in m_notFoundAssetUuids)
200 {
201 uuids.Remove(uuid);
202 }
203  
204 m_log.ErrorFormat(
205 "[ARCHIVER]: Asset service failed to return information about {0} requested assets", uuids.Count);
206  
207 int i = 0;
208 foreach (UUID uuid in uuids)
209 {
210 m_log.ErrorFormat("[ARCHIVER]: No information about asset {0} received", uuid);
211  
212 if (++i >= MAX_UUID_DISPLAY_ON_TIMEOUT)
213 break;
214 }
215  
216 if (uuids.Count > MAX_UUID_DISPLAY_ON_TIMEOUT)
217 m_log.ErrorFormat(
218 "[ARCHIVER]: (... {0} more not shown)", uuids.Count - MAX_UUID_DISPLAY_ON_TIMEOUT);
219  
220 m_log.Error("[ARCHIVER]: Archive save aborted. PLEASE DO NOT USE THIS ARCHIVE, IT WILL BE INCOMPLETE.");
221 }
222 catch (Exception e)
223 {
224 m_log.ErrorFormat("[ARCHIVER]: Timeout handler exception {0}{1}", e.Message, e.StackTrace);
225 }
226 finally
227 {
228 if (timedOut)
229 Util.FireAndForget(PerformAssetsRequestCallback, true);
230 }
231 }
232  
233 protected void PreAssetRequestCallback(string fetchedAssetID, object assetType, AssetBase fetchedAsset)
234 {
235 // Check for broken asset types and fix them with the AssetType gleaned by UuidGatherer
236 if (fetchedAsset != null && fetchedAsset.Type == (sbyte)AssetType.Unknown)
237 {
238 m_log.InfoFormat("[ARCHIVER]: Rewriting broken asset type for {0} to {1}", fetchedAsset.ID, SLUtil.AssetTypeFromCode((sbyte)assetType));
239 fetchedAsset.Type = (sbyte)assetType;
240 }
241  
242 AssetRequestCallback(fetchedAssetID, this, fetchedAsset);
243 }
244  
245 /// <summary>
246 /// Called back by the asset cache when it has the asset
247 /// </summary>
248 /// <param name="assetID"></param>
249 /// <param name="asset"></param>
250 public void AssetRequestCallback(string id, object sender, AssetBase asset)
251 {
252 Culture.SetCurrentCulture();
253  
254 try
255 {
256 lock (this)
257 {
258 //m_log.DebugFormat("[ARCHIVER]: Received callback for asset {0}", id);
259  
260 m_requestCallbackTimer.Stop();
261  
262 if ((m_requestState == RequestState.Aborted) || (m_requestState == RequestState.Completed))
263 {
264 m_log.WarnFormat(
265 "[ARCHIVER]: Received information about asset {0} while in state {1}. Ignoring.",
266 id, m_requestState);
267  
268 return;
269 }
270  
271 if (asset != null)
272 {
273 if (m_options.ContainsKey("verbose"))
274 m_log.InfoFormat("[ARCHIVER]: Writing asset {0}", id);
275  
276 m_foundAssetUuids.Add(asset.FullID);
277  
278 m_assetsArchiver.WriteAsset(PostProcess(asset));
279 }
280 else
281 {
282 if (m_options.ContainsKey("verbose"))
283 m_log.InfoFormat("[ARCHIVER]: Recording asset {0} as not found", id);
284  
285 m_notFoundAssetUuids.Add(new UUID(id));
286 }
287  
288 if (m_foundAssetUuids.Count + m_notFoundAssetUuids.Count >= m_repliesRequired)
289 {
290 m_requestState = RequestState.Completed;
291  
292 m_log.DebugFormat(
293 "[ARCHIVER]: Successfully added {0} assets ({1} assets not found but these may be expected invalid references)",
294 m_foundAssetUuids.Count, m_notFoundAssetUuids.Count);
295  
296 // We want to stop using the asset cache thread asap
297 // as we now need to do the work of producing the rest of the archive
298 Util.FireAndForget(PerformAssetsRequestCallback, false);
299 }
300 else
301 {
302 m_requestCallbackTimer.Start();
303 }
304 }
305 }
306 catch (Exception e)
307 {
308 m_log.ErrorFormat("[ARCHIVER]: AssetRequestCallback failed with {0}", e);
309 }
310 }
311  
312 /// <summary>
313 /// Perform the callback on the original requester of the assets
314 /// </summary>
315 protected void PerformAssetsRequestCallback(object o)
316 {
317 Culture.SetCurrentCulture();
318  
319 Boolean timedOut = (Boolean)o;
320  
321 try
322 {
323 m_assetsRequestCallback(m_foundAssetUuids, m_notFoundAssetUuids, timedOut);
324 }
325 catch (Exception e)
326 {
327 m_log.ErrorFormat(
328 "[ARCHIVER]: Terminating archive creation since asset requster callback failed with {0}", e);
329 }
330 }
331  
332 protected AssetBase PostProcess(AssetBase asset)
333 {
334 if (asset.Type == (sbyte)AssetType.Object && asset.Data != null && m_options.ContainsKey("home"))
335 {
336 //m_log.DebugFormat("[ARCHIVER]: Rewriting object data for {0}", asset.ID);
337 string xml = ExternalRepresentationUtils.RewriteSOP(Utils.BytesToString(asset.Data), m_options["home"].ToString(), m_userAccountService, m_scopeID);
338 asset.Data = Utils.StringToBytes(xml);
339 }
340 return asset;
341 }
342 }
343 }