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.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 /// <summary>
235 /// Creates a new asset
236 /// </summary>
237 /// Returns a random ID if none is passed into it
238 /// <param name="asset"></param>
239 /// <returns></returns>
240 public string Store(AssetBase asset)
241 {
242 if (String.IsNullOrEmpty(m_serverUrl))
243 {
244 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
245 throw new InvalidOperationException();
246 }
247  
248 bool storedInCache = false;
249  
250 // AssetID handling
251 if (String.IsNullOrEmpty(asset.ID) || asset.ID == ZeroID)
252 {
253 asset.FullID = UUID.Random();
254 asset.ID = asset.FullID.ToString();
255 }
256  
257 // Cache handling
258 if (m_cache != null)
259 {
260 m_cache.Cache(asset);
261 storedInCache = true;
262 }
263  
264 // Local asset handling
265 if (asset.Local)
266 {
267 if (!storedInCache)
268 {
269 m_log.Error("Cannot store local " + asset.Metadata.ContentType + " asset without an asset cache");
270 asset.ID = null;
271 asset.FullID = UUID.Zero;
272 }
273  
274 return asset.ID;
275 }
276  
277 return SimianStoreOperation(asset);
278 }
279  
280 /// <summary>
281 /// Update an asset's content
282 /// </summary>
283 /// Attachments and bare scripts need this!!
284 /// <param name="id"> </param>
285 /// <param name="data"></param>
286 /// <returns></returns>
287 public bool UpdateContent(string id, byte[] data)
288 {
289 if (String.IsNullOrEmpty(m_serverUrl))
290 {
291 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
292 throw new InvalidOperationException();
293 }
294  
295 AssetBase asset = Get(id);
296  
297 if (asset == null)
298 {
299 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to fetch asset {0} for updating", id);
300 return false;
301 }
302  
303 asset.Data = data;
304  
305 string result = Store(asset);
306 return !String.IsNullOrEmpty(result);
307 }
308  
309 /// <summary>
310 /// Delete an asset
311 /// </summary>
312 /// <param name="id"></param>
313 /// <returns></returns>
314 public bool Delete(string id)
315 {
316 if (String.IsNullOrEmpty(m_serverUrl))
317 {
318 m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured");
319 throw new InvalidOperationException();
320 }
321  
322 if (m_cache != null)
323 m_cache.Expire(id);
324  
325 return SimianDeleteOperation(id);
326 }
327  
328 #endregion IAssetService
329  
330 #region SimianOperations
331 /// <summary>
332 /// Invokes the xRemoveAsset operation on the simian server to delete an asset
333 /// </summary>
334 /// <param name="id"></param>
335 /// <returns></returns>
336 private bool SimianDeleteOperation(string id)
337 {
338 try
339 {
340 NameValueCollection requestArgs = new NameValueCollection
341 {
342 { "RequestMethod", "xRemoveAsset" },
343 { "AssetID", id }
344 };
345  
346 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
347 if (! response["Success"].AsBoolean())
348 {
349 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to delete asset; {0}",response["Message"].AsString());
350 return false;
351 }
352  
353 return true;
354  
355 }
356 catch (Exception ex)
357 {
358 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to delete asset {0}; {1}", id, ex.Message);
359 }
360  
361 return false;
362 }
363  
364 /// <summary>
365 /// Invokes the xAddAsset operation on the simian server to create or update an asset
366 /// </summary>
367 /// <param name="id"></param>
368 /// <returns></returns>
369 private string SimianStoreOperation(AssetBase asset)
370 {
371 try
372 {
373 NameValueCollection requestArgs = new NameValueCollection
374 {
375 { "RequestMethod", "xAddAsset" },
376 { "ContentType", asset.Metadata.ContentType },
377 { "EncodedData", Convert.ToBase64String(asset.Data) },
378 { "AssetID", asset.FullID.ToString() },
379 { "CreatorID", asset.Metadata.CreatorID },
380 { "Temporary", asset.Temporary ? "1" : "0" },
381 { "Name", asset.Name }
382 };
383  
384 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
385 if (! response["Success"].AsBoolean())
386 {
387 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR] failed to store asset; {0}",response["Message"].AsString());
388 return null;
389 }
390  
391 // asset.ID is always set before calling this function
392 return asset.ID;
393  
394 }
395 catch (Exception ex)
396 {
397 m_log.ErrorFormat("[SIMIAN ASSET CONNECTOR] failed to store asset; {0}",ex.Message);
398 }
399  
400 return null;
401 }
402  
403 /// <summary>
404 /// Invokes the xGetAsset operation on the simian server to get data associated with an asset
405 /// </summary>
406 /// <param name="id"></param>
407 /// <returns></returns>
408 private AssetBase SimianGetOperation(string id)
409 {
410 try
411 {
412 NameValueCollection requestArgs = new NameValueCollection
413 {
414 { "RequestMethod", "xGetAsset" },
415 { "ID", id }
416 };
417  
418 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
419 if (! response["Success"].AsBoolean())
420 {
421 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR] Failed to get asset; {0}",response["Message"].AsString());
422 return null;
423 }
424  
425 AssetBase asset = new AssetBase();
426  
427 asset.ID = id;
428 asset.Name = String.Empty;
429 asset.Metadata.ContentType = response["ContentType"].AsString(); // this will also set the asset Type property
430 asset.CreatorID = response["CreatorID"].AsString();
431 asset.Data = System.Convert.FromBase64String(response["EncodedData"].AsString());
432 asset.Local = false;
433 asset.Temporary = response["Temporary"];
434  
435 return asset;
436 }
437 catch (Exception ex)
438 {
439 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to retrieve asset {0}; {1}", id, ex.Message);
440 }
441  
442 return null;
443 }
444  
445 /// <summary>
446 /// Invokes the xGetAssetMetadata operation on the simian server to retrieve metadata for an asset
447 /// This operation is generally used to determine if an asset exists in the database
448 /// </summary>
449 /// <param name="id"></param>
450 /// <returns></returns>
451 private AssetMetadata SimianGetMetadataOperation(string id)
452 {
453 try
454 {
455 NameValueCollection requestArgs = new NameValueCollection
456 {
457 { "RequestMethod", "xGetAssetMetadata" },
458 { "ID", id }
459 };
460  
461 OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs);
462 if (! response["Success"].AsBoolean())
463 {
464 // this is not really an error, this call is used to test existence
465 // m_log.DebugFormat("[SIMIAN ASSET CONNECTOR] Failed to get asset metadata; {0}",response["Message"].AsString());
466 return null;
467 }
468  
469 AssetMetadata metadata = new AssetMetadata();
470 metadata.ID = id;
471 metadata.ContentType = response["ContentType"].AsString();
472 metadata.CreatorID = response["CreatorID"].AsString();
473 metadata.Local = false;
474 metadata.Temporary = response["Temporary"];
475  
476 string lastModifiedStr = response["Last-Modified"].AsString();
477 if (! String.IsNullOrEmpty(lastModifiedStr))
478 {
479 DateTime lastModified;
480 if (DateTime.TryParse(lastModifiedStr, out lastModified))
481 metadata.CreationDate = lastModified;
482 }
483  
484 return metadata;
485 }
486 catch (Exception ex)
487 {
488 m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to get asset metadata; {0}", ex.Message);
489 }
490  
491 return null;
492 }
493 #endregion
494  
495 // private AssetMetadata GetRemoteMetadata(string id)
496 // {
497 // Uri url;
498 // AssetMetadata metadata = null;
499  
500 // // Determine if id is an absolute URL or a grid-relative UUID
501 // if (!Uri.TryCreate(id, UriKind.Absolute, out url))
502 // url = new Uri(m_serverUrl + id);
503  
504 // try
505 // {
506 // HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
507 // request.Method = "HEAD";
508  
509 // using (WebResponse response = request.GetResponse())
510 // {
511 // using (Stream responseStream = response.GetResponseStream())
512 // {
513 // // Create the metadata object
514 // metadata = new AssetMetadata();
515 // metadata.ContentType = response.ContentType;
516 // metadata.ID = id;
517  
518 // UUID uuid;
519 // if (UUID.TryParse(id, out uuid))
520 // metadata.FullID = uuid;
521  
522 // string lastModifiedStr = response.Headers.Get("Last-Modified");
523 // if (!String.IsNullOrEmpty(lastModifiedStr))
524 // {
525 // DateTime lastModified;
526 // if (DateTime.TryParse(lastModifiedStr, out lastModified))
527 // metadata.CreationDate = lastModified;
528 // }
529 // }
530 // }
531 // }
532 // catch (Exception ex)
533 // {
534 // m_log.Warn("[SIMIAN ASSET CONNECTOR]: Asset HEAD from " + url + " failed: " + ex.Message);
535 // }
536  
537 // return metadata;
538 // }
539  
540 // private AssetBase GetRemote(string id)
541 // {
542 // AssetBase asset = null;
543 // Uri url;
544  
545 // // Determine if id is an absolute URL or a grid-relative UUID
546 // if (!Uri.TryCreate(id, UriKind.Absolute, out url))
547 // url = new Uri(m_serverUrl + id);
548  
549 // try
550 // {
551 // HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
552  
553 // using (WebResponse response = request.GetResponse())
554 // {
555 // using (Stream responseStream = response.GetResponseStream())
556 // {
557 // string creatorID = response.Headers.GetOne("X-Asset-Creator-Id") ?? String.Empty;
558  
559 // // Create the asset object
560 // asset = new AssetBase(id, String.Empty, SLUtil.ContentTypeToSLAssetType(response.ContentType), creatorID);
561  
562 // UUID assetID;
563 // if (UUID.TryParse(id, out assetID))
564 // asset.FullID = assetID;
565  
566 // // Grab the asset data from the response stream
567 // using (MemoryStream stream = new MemoryStream())
568 // {
569 // responseStream.CopyStream(stream, Int32.MaxValue);
570 // asset.Data = stream.ToArray();
571 // }
572 // }
573 // }
574  
575 // // Cache store
576 // if (m_cache != null && asset != null)
577 // m_cache.Cache(asset);
578  
579 // return asset;
580 // }
581 // catch (Exception ex)
582 // {
583 // m_log.Warn("[SIMIAN ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message);
584 // return null;
585 // }
586 // }
587  
588 // private string StoreRemote(AssetBase asset)
589 // {
590 // // Distinguish public and private assets
591 // bool isPublic = true;
592 // switch ((AssetType)asset.Type)
593 // {
594 // case AssetType.CallingCard:
595 // case AssetType.Gesture:
596 // case AssetType.LSLBytecode:
597 // case AssetType.LSLText:
598 // isPublic = false;
599 // break;
600 // }
601  
602 // string errorMessage = null;
603  
604 // // Build the remote storage request
605 // List<MultipartForm.Element> postParameters = new List<MultipartForm.Element>()
606 // {
607 // new MultipartForm.Parameter("AssetID", asset.FullID.ToString()),
608 // new MultipartForm.Parameter("CreatorID", asset.Metadata.CreatorID),
609 // new MultipartForm.Parameter("Temporary", asset.Temporary ? "1" : "0"),
610 // new MultipartForm.Parameter("Public", isPublic ? "1" : "0"),
611 // new MultipartForm.File("Asset", asset.Name, asset.Metadata.ContentType, asset.Data)
612 // };
613  
614 // // Make the remote storage request
615 // try
616 // {
617 // // Simian does not require the asset ID to be in the URL because it's in the post data.
618 // // By appending it to the URL also, we allow caching proxies (squid) to invalidate asset URLs
619 // HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_serverUrl + asset.FullID.ToString());
620  
621 // using (HttpWebResponse response = MultipartForm.Post(request, postParameters))
622 // {
623 // using (Stream responseStream = response.GetResponseStream())
624 // {
625 // string responseStr = null;
626  
627 // try
628 // {
629 // responseStr = responseStream.GetStreamString();
630 // OSD responseOSD = OSDParser.Deserialize(responseStr);
631 // if (responseOSD.Type == OSDType.Map)
632 // {
633 // OSDMap responseMap = (OSDMap)responseOSD;
634 // if (responseMap["Success"].AsBoolean())
635 // return asset.ID;
636 // else
637 // errorMessage = "Upload failed: " + responseMap["Message"].AsString();
638 // }
639 // else
640 // {
641 // errorMessage = "Response format was invalid:\n" + responseStr;
642 // }
643 // }
644 // catch (Exception ex)
645 // {
646 // if (!String.IsNullOrEmpty(responseStr))
647 // errorMessage = "Failed to parse the response:\n" + responseStr;
648 // else
649 // errorMessage = "Failed to retrieve the response: " + ex.Message;
650 // }
651 // }
652 // }
653 // }
654 // catch (WebException ex)
655 // {
656 // errorMessage = ex.Message;
657 // }
658  
659 // m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to store asset \"{0}\" ({1}, {2}): {3}",
660 // asset.Name, asset.ID, asset.Metadata.ContentType, errorMessage);
661  
662 // return null;
663 // }
664 }
665 }