clockwerk-opensim-stable – 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.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, AssetType> 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, AssetType> 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, AssetType> 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 AssetType type = (AssetType)assetType;
239 m_log.InfoFormat("[ARCHIVER]: Rewriting broken asset type for {0} to {1}", fetchedAsset.ID, type);
240 fetchedAsset.Type = (sbyte)type;
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 Util.FireAndForget(PerformAssetsRequestCallback, 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 }