clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 {
43 /// <summary>
44 /// Encapsulate the asynchronous requests for the assets required for an archive operation
45 /// </summary>
46 class AssetsRequest
47 {
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49  
50 /// <summary>
51 /// Method called when all the necessary assets for an archive request have been received.
52 /// </summary>
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  
64 /// <value>
65 /// Timeout threshold if we still need assets or missing asset notifications but have stopped receiving them
66 /// from the asset service
67 /// </value>
68 protected const int TIMEOUT = 60 * 1000;
69  
70 /// <value>
71 /// If a timeout does occur, limit the amount of UUID information put to the console.
72 /// </value>
73 protected const int MAX_UUID_DISPLAY_ON_TIMEOUT = 3;
74  
75 protected System.Timers.Timer m_requestCallbackTimer;
76  
77 /// <value>
78 /// State of this request
79 /// </value>
80 private RequestState m_requestState = RequestState.Initial;
81  
82 /// <value>
83 /// uuids to request
84 /// </value>
85 protected IDictionary<UUID, sbyte> m_uuids;
86  
87 /// <value>
88 /// Callback used when all the assets requested have been received.
89 /// </value>
90 protected AssetsRequestCallback m_assetsRequestCallback;
91  
92 /// <value>
93 /// List of assets that were found. This will be passed back to the requester.
94 /// </value>
95 protected List<UUID> m_foundAssetUuids = new List<UUID>();
96  
97 /// <value>
98 /// Maintain a list of assets that could not be found. This will be passed back to the requester.
99 /// </value>
100 protected List<UUID> m_notFoundAssetUuids = new List<UUID>();
101  
102 /// <value>
103 /// Record the number of asset replies required so we know when we've finished
104 /// </value>
105 private int m_repliesRequired;
106  
107 /// <value>
108 /// Asset service used to request the assets
109 /// </value>
110 protected IAssetService m_assetService;
111 protected IUserAccountService m_userAccountService;
112 protected UUID m_scopeID; // the grid ID
113  
114 protected AssetsArchiver m_assetsArchiver;
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 Watchdog.RunInThread(PerformAssetsRequestCallback, "Archive Assets Request Callback", true);
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  
246 /// <summary>
247 /// Called back by the asset cache when it has the asset
248 /// </summary>
249 /// <param name="assetID"></param>
250 /// <param name="asset"></param>
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 if (m_options.ContainsKey("verbose"))
275 m_log.InfoFormat("[ARCHIVER]: Writing asset {0}", id);
276  
277 m_foundAssetUuids.Add(asset.FullID);
278  
279 m_assetsArchiver.WriteAsset(PostProcess(asset));
280 }
281 else
282 {
283 if (m_options.ContainsKey("verbose"))
284 m_log.InfoFormat("[ARCHIVER]: Recording asset {0} as not found", id);
285  
286 m_notFoundAssetUuids.Add(new UUID(id));
287 }
288  
289 if (m_foundAssetUuids.Count + m_notFoundAssetUuids.Count >= m_repliesRequired)
290 {
291 m_requestState = RequestState.Completed;
292  
293 m_log.DebugFormat(
294 "[ARCHIVER]: Successfully added {0} assets ({1} assets not found but these may be expected invalid references)",
295 m_foundAssetUuids.Count, m_notFoundAssetUuids.Count);
296  
297 // We want to stop using the asset cache thread asap
298 // as we now need to do the work of producing the rest of the archive
299 Watchdog.RunInThread(PerformAssetsRequestCallback, "Archive Assets Request Callback", false);
300 }
301 else
302 {
303 m_requestCallbackTimer.Start();
304 }
305 }
306 }
307 catch (Exception e)
308 {
309 m_log.ErrorFormat("[ARCHIVER]: AssetRequestCallback failed with {0}", e);
310 }
311 }
312  
313 /// <summary>
314 /// Perform the callback on the original requester of the assets
315 /// </summary>
316 protected void PerformAssetsRequestCallback(object o)
317 {
318 Culture.SetCurrentCulture();
319  
320 Boolean timedOut = (Boolean)o;
321  
322 try
323 {
324 m_assetsRequestCallback(m_foundAssetUuids, m_notFoundAssetUuids, timedOut);
325 }
326 catch (Exception e)
327 {
328 m_log.ErrorFormat(
329 "[ARCHIVER]: Terminating archive creation since asset requster callback failed with {0}", e);
330 }
331 }
332  
333 protected AssetBase PostProcess(AssetBase asset)
334 {
335 if (asset.Type == (sbyte)AssetType.Object && asset.Data != null && m_options.ContainsKey("home"))
336 {
337 //m_log.DebugFormat("[ARCHIVER]: Rewriting object data for {0}", asset.ID);
338 string xml = ExternalRepresentationUtils.RewriteSOP(Utils.BytesToString(asset.Data), m_options["home"].ToString(), m_userAccountService, m_scopeID);
339 asset.Data = Utils.StringToBytes(xml);
340 }
341 return asset;
342 }
343 }
344 }