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.Collections.Specialized;
31 using System.IO;
32 using System.Net;
33 using System.Reflection;
34 using log4net;
35 using Mono.Addins;
36 using Nini.Config;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 using OpenSim.Services.Interfaces;
41 using OpenMetaverse;
42 using OpenMetaverse.StructuredData;
43  
44 namespace OpenSim.Services.Connectors.SimianGrid
45 {
46 /// <summary>
47 /// Connects to the SimianGrid asset service
48 /// </summary>
49 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianAssetServiceConnector")]
50 public class SimianAssetServiceConnector : IAssetService, ISharedRegionModule
51 {
52 private static readonly ILog m_log =
53 LogManager.GetLogger(
54 MethodBase.GetCurrentMethod().DeclaringType);
55 private static string ZeroID = UUID.Zero.ToString();
56  
57 private string m_serverUrl = String.Empty;
58 private IImprovedAssetCache m_cache;
59 private bool m_Enabled = false;
60  
61 #region ISharedRegionModule
62  
63 public Type ReplaceableInterface { get { return null; } }
64 public void RegionLoaded(Scene scene)
65 {
66 if (m_cache == null)
67 {
68 IImprovedAssetCache cache = scene.RequestModuleInterface<IImprovedAssetCache>();
69 if (cache is ISharedRegionModule)
70 m_cache = cache;
71 }
72 }
73 public void PostInitialise() { }
74 public void Close() { }
75  
76 public SimianAssetServiceConnector() { }
77 public string Name { get { return "SimianAssetServiceConnector"; } }
78 public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface<IAssetService>(this); } }
79 public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface<IAssetService>(this); } }
80  
81 #endregion ISharedRegionModule
82  
83 public SimianAssetServiceConnector(IConfigSource source)
84 {
85 CommonInit(source);
86 }
87  
88 public SimianAssetServiceConnector(string url)
89 {
90 if (!url.EndsWith("/") && !url.EndsWith("="))
91 url = url + '/';
92 m_serverUrl = url;
93 }
94  
95 public void Initialise(IConfigSource source)
96 {
97 IConfig moduleConfig = source.Configs["Modules"];
98 if (moduleConfig != null)
99 {
100 string name = moduleConfig.GetString("AssetServices", "");
101 if (name == Name)
102 CommonInit(source);
103 }
104 }
105  
106 private void CommonInit(IConfigSource source)
107 {
108 IConfig gridConfig = source.Configs["AssetService"];
109 if (gridConfig != null)
110 {
111 string serviceUrl = gridConfig.GetString("AssetServerURI");
112 if (!String.IsNullOrEmpty(serviceUrl))
113 {
114 if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("="))
115 serviceUrl = serviceUrl + '/';
116 m_serverUrl = serviceUrl;
117 }
118 }
119  
120 if (String.IsNullOrEmpty(m_serverUrl))
121 m_log.Info("[SIMIAN ASSET CONNECTOR]: No AssetServerURI specified, disabling connector");
122 else
123 m_Enabled = true;
124 }
125  
126 #region IAssetService
127  
128 public AssetBase Get(string id)
129 {
130 if (String.IsNullOrEmpty(m_serverUrl))
131 {
132 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
133 throw new InvalidOperationException();
134 }
135  
136 // Cache fetch
137 if (m_cache != null)
138 {
139 AssetBase asset = m_cache.Get(id);
140 if (asset != null)
141 return asset;
142 }
143  
144 return SimianGetOperation(id);
145 }
146  
147  
148 public AssetBase GetCached(string id)
149 {
150 if (m_cache != null)
151 return m_cache.Get(id);
152  
153 return null;
154 }
155  
156 /// <summary>
157 /// Get an asset's metadata
158 /// </summary>
159 /// <param name="id"></param>
160 /// <returns></returns>
161 public AssetMetadata GetMetadata(string id)
162 {
163 if (String.IsNullOrEmpty(m_serverUrl))
164 {
165 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
166 throw new InvalidOperationException();
167 }
168  
169 // Cache fetch
170 if (m_cache != null)
171 {
172 AssetBase asset = m_cache.Get(id);
173 if (asset != null)
174 return asset.Metadata;
175 }
176  
177 // return GetRemoteMetadata(id);
178 return SimianGetMetadataOperation(id);
179 }
180  
181 public byte[] GetData(string id)
182 {
183 if (String.IsNullOrEmpty(m_serverUrl))
184 {
185 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
186 throw new InvalidOperationException();
187 }
188  
189 AssetBase asset = Get(id);
190  
191 if (asset != null)
192 return asset.Data;
193  
194 return null;
195 }
196  
197 /// <summary>
198 /// Get an asset asynchronously
199 /// </summary>
200 /// <param name="id">The asset id</param>
201 /// <param name="sender">Represents the requester. Passed back via the handler</param>
202 /// <param name="handler">The handler to call back once the asset has been retrieved</param>
203 /// <returns>True if the id was parseable, false otherwise</returns>
204 public bool Get(string id, Object sender, AssetRetrieved handler)
205 {
206 if (String.IsNullOrEmpty(m_serverUrl))
207 {
208 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
209 throw new InvalidOperationException();
210 }
211  
212 // Cache fetch
213 if (m_cache != null)
214 {
215 AssetBase asset = m_cache.Get(id);
216 if (asset != null)
217 {
218 handler(id, sender, asset);
219 return true;
220 }
221 }
222  
223 Util.FireAndForget(
224 delegate(object o)
225 {
226 AssetBase asset = SimianGetOperation(id);
227 handler(id, sender, asset);
228 }
229 );
230  
231 return true;
232 }
233  
234 public bool[] AssetsExist(string[] ids)
235 {
236 if (String.IsNullOrEmpty(m_serverUrl))
237 {
238 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
239 throw new InvalidOperationException();
240 }
241  
242 bool[] exist = new bool[ids.Length];
243  
244 for (int i = 0; i < ids.Length; i++)
245 {
246 AssetMetadata metadata = GetMetadata(ids[i]);
247 if (metadata != null)
248 exist[i] = true;
249 }
250  
251 return exist;
252 }
253  
254 /// <summary>
255 /// Creates a new asset
256 /// </summary>
257 /// Returns a random ID if none is passed into it
258 /// <param name="asset"></param>
259 /// <returns></returns>
260 public string Store(AssetBase asset)
261 {
262 if (String.IsNullOrEmpty(m_serverUrl))
263 {
264 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
265 throw new InvalidOperationException();
266 }
267  
268 bool storedInCache = false;
269  
270 // AssetID handling
271 if (String.IsNullOrEmpty(asset.ID) || asset.ID == ZeroID)
272 {
273 asset.FullID = UUID.Random();
274 asset.ID = asset.FullID.ToString();
275 }
276  
277 // Cache handling
278 if (m_cache != null)
279 {
280 m_cache.Cache(asset);
281 storedInCache = true;
282 }
283  
284 // Local asset handling
285 if (asset.Local)
286 {
287 if (!storedInCache)
288 {
289 m_log.Error("Cannot store local " + asset.Metadata.ContentType + " asset without an asset cache");
290 asset.ID = null;
291 asset.FullID = UUID.Zero;
292 }
293  
294 return asset.ID;
295 }
296  
297 return SimianStoreOperation(asset);
298 }
299  
300 /// <summary>
301 /// Update an asset's content
302 /// </summary>
303 /// Attachments and bare scripts need this!!
304 /// <param name="id"> </param>
305 /// <param name="data"></param>
306 /// <returns></returns>
307 public bool UpdateContent(string id, byte[] data)
308 {
309 if (String.IsNullOrEmpty(m_serverUrl))
310 {
311 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
312 throw new InvalidOperationException();
313 }
314  
315 AssetBase asset = Get(id);
316  
317 if (asset == null)
318 {
319 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to fetch asset {0} for updating", id);
320 return false;
321 }
322  
323 asset.Data = data;
324  
325 string result = Store(asset);
326 return !String.IsNullOrEmpty(result);
327 }
328  
329 /// <summary>
330 /// Delete an asset
331 /// </summary>
332 /// <param name="id"></param>
333 /// <returns></returns>
334 public bool Delete(string id)
335 {
336 if (String.IsNullOrEmpty(m_serverUrl))
337 {
338 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
339 throw new InvalidOperationException();
340 }
341  
342 if (m_cache != null)
343 m_cache.Expire(id);
344  
345 return SimianDeleteOperation(id);
346 }
347  
348 #endregion IAssetService
349  
350 #region SimianOperations
351 /// <summary>
352 /// Invokes the xRemoveAsset operation on the simian server to delete an asset
353 /// </summary>
354 /// <param name="id"></param>
355 /// <returns></returns>
356 private bool SimianDeleteOperation(string id)
357 {
358 try
359 {
360 NameValueCollection requestArgs = new NameValueCollection
361 {
362 { "RequestMethod", "xRemoveAsset" },
363 { "AssetID", id }
364 };
365  
366 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
367 if (! response["Success"].AsBoolean())
368 {
369 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to delete asset; {0}",response["Message"].AsString());
370 return false;
371 }
372  
373 return true;
374  
375 }
376 catch (Exception ex)
377 {
378 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to delete asset {0}; {1}", id, ex.Message);
379 }
380  
381 return false;
382 }
383  
384 /// <summary>
385 /// Invokes the xAddAsset operation on the simian server to create or update an asset
386 /// </summary>
387 /// <param name="id"></param>
388 /// <returns></returns>
389 private string SimianStoreOperation(AssetBase asset)
390 {
391 try
392 {
393 NameValueCollection requestArgs = new NameValueCollection
394 {
395 { "RequestMethod", "xAddAsset" },
396 { "ContentType", asset.Metadata.ContentType },
397 { "EncodedData", Convert.ToBase64String(asset.Data) },
398 { "AssetID", asset.FullID.ToString() },
399 { "CreatorID", asset.Metadata.CreatorID },
400 { "Temporary", asset.Temporary ? "1" : "0" },
401 { "Name", asset.Name }
402 };
403  
404 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
405 if (! response["Success"].AsBoolean())
406 {
407 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR] failed to store asset; {0}",response["Message"].AsString());
408 return null;
409 }
410  
411 // asset.ID is always set before calling this function
412 return asset.ID;
413  
414 }
415 catch (Exception ex)
416 {
417 m_log.ErrorFormat("[SIMIAN ASSET CONNECTOR] failed to store asset; {0}",ex.Message);
418 }
419  
420 return null;
421 }
422  
423 /// <summary>
424 /// Invokes the xGetAsset operation on the simian server to get data associated with an asset
425 /// </summary>
426 /// <param name="id"></param>
427 /// <returns></returns>
428 private AssetBase SimianGetOperation(string id)
429 {
430 try
431 {
432 NameValueCollection requestArgs = new NameValueCollection
433 {
434 { "RequestMethod", "xGetAsset" },
435 { "ID", id }
436 };
437  
438 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
439 if (! response["Success"].AsBoolean())
440 {
441 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR] Failed to get asset; {0}",response["Message"].AsString());
442 return null;
443 }
444  
445 AssetBase asset = new AssetBase();
446  
447 asset.ID = id;
448 asset.Name = String.Empty;
449 asset.Metadata.ContentType = response["ContentType"].AsString(); // this will also set the asset Type property
450 asset.CreatorID = response["CreatorID"].AsString();
451 asset.Data = System.Convert.FromBase64String(response["EncodedData"].AsString());
452 asset.Local = false;
453 asset.Temporary = response["Temporary"];
454  
455 return asset;
456 }
457 catch (Exception ex)
458 {
459 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to retrieve asset {0}; {1}", id, ex.Message);
460 }
461  
462 return null;
463 }
464  
465 /// <summary>
466 /// Invokes the xGetAssetMetadata operation on the simian server to retrieve metadata for an asset
467 /// This operation is generally used to determine if an asset exists in the database
468 /// </summary>
469 /// <param name="id"></param>
470 /// <returns></returns>
471 private AssetMetadata SimianGetMetadataOperation(string id)
472 {
473 try
474 {
475 NameValueCollection requestArgs = new NameValueCollection
476 {
477 { "RequestMethod", "xGetAssetMetadata" },
478 { "ID", id }
479 };
480  
481 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
482 if (! response["Success"].AsBoolean())
483 {
484 // this is not really an error, this call is used to test existence
485 // m_log.DebugFormat("[SIMIAN ASSET CONNECTOR] Failed to get asset metadata; {0}",response["Message"].AsString());
486 return null;
487 }
488  
489 AssetMetadata metadata = new AssetMetadata();
490 metadata.ID = id;
491 metadata.ContentType = response["ContentType"].AsString();
492 metadata.CreatorID = response["CreatorID"].AsString();
493 metadata.Local = false;
494 metadata.Temporary = response["Temporary"];
495  
496 string lastModifiedStr = response["Last-Modified"].AsString();
497 if (! String.IsNullOrEmpty(lastModifiedStr))
498 {
499 DateTime lastModified;
500 if (DateTime.TryParse(lastModifiedStr, out lastModified))
501 metadata.CreationDate = lastModified;
502 }
503  
504 return metadata;
505 }
506 catch (Exception ex)
507 {
508 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to get asset metadata; {0}", ex.Message);
509 }
510  
511 return null;
512 }
513 #endregion
514  
515 // private AssetMetadata GetRemoteMetadata(string id)
516 // {
517 // Uri url;
518 // AssetMetadata metadata = null;
519  
520 // // Determine if id is an absolute URL or a grid-relative UUID
521 // if (!Uri.TryCreate(id, UriKind.Absolute, out url))
522 // url = new Uri(m_serverUrl + id);
523  
524 // try
525 // {
526 // HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
527 // request.Method = "HEAD";
528  
529 // using (WebResponse response = request.GetResponse())
530 // {
531 // using (Stream responseStream = response.GetResponseStream())
532 // {
533 // // Create the metadata object
534 // metadata = new AssetMetadata();
535 // metadata.ContentType = response.ContentType;
536 // metadata.ID = id;
537  
538 // UUID uuid;
539 // if (UUID.TryParse(id, out uuid))
540 // metadata.FullID = uuid;
541  
542 // string lastModifiedStr = response.Headers.Get("Last-Modified");
543 // if (!String.IsNullOrEmpty(lastModifiedStr))
544 // {
545 // DateTime lastModified;
546 // if (DateTime.TryParse(lastModifiedStr, out lastModified))
547 // metadata.CreationDate = lastModified;
548 // }
549 // }
550 // }
551 // }
552 // catch (Exception ex)
553 // {
554 // m_log.Warn("[SIMIAN ASSET CONNECTOR]: Asset HEAD from " + url + " failed: " + ex.Message);
555 // }
556  
557 // return metadata;
558 // }
559  
560 // private AssetBase GetRemote(string id)
561 // {
562 // AssetBase asset = null;
563 // Uri url;
564  
565 // // Determine if id is an absolute URL or a grid-relative UUID
566 // if (!Uri.TryCreate(id, UriKind.Absolute, out url))
567 // url = new Uri(m_serverUrl + id);
568  
569 // try
570 // {
571 // HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
572  
573 // using (WebResponse response = request.GetResponse())
574 // {
575 // using (Stream responseStream = response.GetResponseStream())
576 // {
577 // string creatorID = response.Headers.GetOne("X-Asset-Creator-Id") ?? String.Empty;
578  
579 // // Create the asset object
580 // asset = new AssetBase(id, String.Empty, SLUtil.ContentTypeToSLAssetType(response.ContentType), creatorID);
581  
582 // UUID assetID;
583 // if (UUID.TryParse(id, out assetID))
584 // asset.FullID = assetID;
585  
586 // // Grab the asset data from the response stream
587 // using (MemoryStream stream = new MemoryStream())
588 // {
589 // responseStream.CopyStream(stream, Int32.MaxValue);
590 // asset.Data = stream.ToArray();
591 // }
592 // }
593 // }
594  
595 // // Cache store
596 // if (m_cache != null && asset != null)
597 // m_cache.Cache(asset);
598  
599 // return asset;
600 // }
601 // catch (Exception ex)
602 // {
603 // m_log.Warn("[SIMIAN ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message);
604 // return null;
605 // }
606 // }
607  
608 // private string StoreRemote(AssetBase asset)
609 // {
610 // // Distinguish public and private assets
611 // bool isPublic = true;
612 // switch ((AssetType)asset.Type)
613 // {
614 // case AssetType.CallingCard:
615 // case AssetType.Gesture:
616 // case AssetType.LSLBytecode:
617 // case AssetType.LSLText:
618 // isPublic = false;
619 // break;
620 // }
621  
622 // string errorMessage = null;
623  
624 // // Build the remote storage request
625 // List<MultipartForm.Element> postParameters = new List<MultipartForm.Element>()
626 // {
627 // new MultipartForm.Parameter("AssetID", asset.FullID.ToString()),
628 // new MultipartForm.Parameter("CreatorID", asset.Metadata.CreatorID),
629 // new MultipartForm.Parameter("Temporary", asset.Temporary ? "1" : "0"),
630 // new MultipartForm.Parameter("Public", isPublic ? "1" : "0"),
631 // new MultipartForm.File("Asset", asset.Name, asset.Metadata.ContentType, asset.Data)
632 // };
633  
634 // // Make the remote storage request
635 // try
636 // {
637 // // Simian does not require the asset ID to be in the URL because it's in the post data.
638 // // By appending it to the URL also, we allow caching proxies (squid) to invalidate asset URLs
639 // HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_serverUrl + asset.FullID.ToString());
640  
641 // using (HttpWebResponse response = MultipartForm.Post(request, postParameters))
642 // {
643 // using (Stream responseStream = response.GetResponseStream())
644 // {
645 // string responseStr = null;
646  
647 // try
648 // {
649 // responseStr = responseStream.GetStreamString();
650 // OSD responseOSD = OSDParser.Deserialize(responseStr);
651 // if (responseOSD.Type == OSDType.Map)
652 // {
653 // OSDMap responseMap = (OSDMap)responseOSD;
654 // if (responseMap["Success"].AsBoolean())
655 // return asset.ID;
656 // else
657 // errorMessage = "Upload failed: " + responseMap["Message"].AsString();
658 // }
659 // else
660 // {
661 // errorMessage = "Response format was invalid:\n" + responseStr;
662 // }
663 // }
664 // catch (Exception ex)
665 // {
666 // if (!String.IsNullOrEmpty(responseStr))
667 // errorMessage = "Failed to parse the response:\n" + responseStr;
668 // else
669 // errorMessage = "Failed to retrieve the response: " + ex.Message;
670 // }
671 // }
672 // }
673 // }
674 // catch (WebException ex)
675 // {
676 // errorMessage = ex.Message;
677 // }
678  
679 // m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to store asset \"{0}\" ({1}, {2}): {3}",
680 // asset.Name, asset.ID, asset.Metadata.ContentType, errorMessage);
681  
682 // return null;
683 // }
684 }
685 }