aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs410
1 files changed, 410 insertions, 0 deletions
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs
new file mode 100644
index 0000000..1c22a72
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs
@@ -0,0 +1,410 @@
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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Net;
32using System.Reflection;
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes;
39using OpenSim.Services.Interfaces;
40using OpenMetaverse;
41using OpenMetaverse.StructuredData;
42
43namespace OpenSim.Services.Connectors.SimianGrid
44{
45 /// <summary>
46 /// Connects to the SimianGrid asset service
47 /// </summary>
48 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
49 public class SimianAssetServiceConnector : IAssetService, ISharedRegionModule
50 {
51 private static readonly ILog m_log =
52 LogManager.GetLogger(
53 MethodBase.GetCurrentMethod().DeclaringType);
54 private static string ZeroID = UUID.Zero.ToString();
55
56 private string m_serverUrl = String.Empty;
57 private IImprovedAssetCache m_cache;
58
59 #region ISharedRegionModule
60
61 public Type ReplaceableInterface { get { return null; } }
62 public void RegionLoaded(Scene scene)
63 {
64 if (m_cache == null)
65 {
66 IImprovedAssetCache cache = scene.RequestModuleInterface<IImprovedAssetCache>();
67 if (cache is ISharedRegionModule)
68 m_cache = cache;
69 }
70 }
71 public void PostInitialise() { }
72 public void Close() { }
73
74 public SimianAssetServiceConnector() { }
75 public string Name { get { return "SimianAssetServiceConnector"; } }
76 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IAssetService>(this); } }
77 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IAssetService>(this); } }
78
79 #endregion ISharedRegionModule
80
81 public SimianAssetServiceConnector(IConfigSource source)
82 {
83 Initialise(source);
84 }
85
86 public void Initialise(IConfigSource source)
87 {
88 if (Simian.IsSimianEnabled(source, "AssetServices"))
89 {
90 IConfig gridConfig = source.Configs["AssetService"];
91 if (gridConfig == null)
92 {
93 m_log.Error("[ASSET CONNECTOR]: AssetService missing from OpenSim.ini");
94 throw new Exception("Asset connector init error");
95 }
96
97 string serviceUrl = gridConfig.GetString("AssetServerURI");
98 if (String.IsNullOrEmpty(serviceUrl))
99 {
100 m_log.Error("[ASSET CONNECTOR]: No AssetServerURI in section AssetService");
101 throw new Exception("Asset connector init error");
102 }
103
104 if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("="))
105 serviceUrl = serviceUrl + '/';
106
107 m_serverUrl = serviceUrl;
108 }
109 }
110
111 #region IAssetService
112
113 public AssetBase Get(string id)
114 {
115 AssetBase asset = null;
116
117 // Cache fetch
118 if (m_cache != null)
119 {
120 asset = m_cache.Get(id);
121 if (asset != null)
122 return asset;
123 }
124
125 Uri url;
126
127 // Determine if id is an absolute URL or a grid-relative UUID
128 if (!Uri.TryCreate(id, UriKind.Absolute, out url))
129 url = new Uri(m_serverUrl + id);
130
131 try
132 {
133 HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
134
135 using (WebResponse response = request.GetResponse())
136 {
137 using (Stream responseStream = response.GetResponseStream())
138 {
139 string creatorID = response.Headers.GetOne("X-Asset-Creator-Id") ?? String.Empty;
140
141 // Create the asset object
142 asset = new AssetBase(id, String.Empty, SLUtil.ContentTypeToSLAssetType(response.ContentType), creatorID);
143
144 UUID assetID;
145 if (UUID.TryParse(id, out assetID))
146 asset.FullID = assetID;
147
148 // Grab the asset data from the response stream
149 using (MemoryStream stream = new MemoryStream())
150 {
151 responseStream.CopyTo(stream, Int32.MaxValue);
152 asset.Data = stream.ToArray();
153 }
154 }
155 }
156
157 // Cache store
158 if (m_cache != null && asset != null)
159 m_cache.Cache(asset);
160
161 return asset;
162 }
163 catch (Exception ex)
164 {
165 m_log.Warn("[ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message);
166 return null;
167 }
168 }
169
170 /// <summary>
171 /// Get an asset's metadata
172 /// </summary>
173 /// <param name="id"></param>
174 /// <returns></returns>
175 public AssetMetadata GetMetadata(string id)
176 {
177 AssetMetadata metadata = null;
178
179 // Cache fetch
180 if (m_cache != null)
181 {
182 AssetBase asset = m_cache.Get(id);
183 if (asset != null)
184 return asset.Metadata;
185 }
186
187 Uri url;
188
189 // Determine if id is an absolute URL or a grid-relative UUID
190 if (!Uri.TryCreate(id, UriKind.Absolute, out url))
191 url = new Uri(m_serverUrl + id);
192
193 try
194 {
195 HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
196 request.Method = "HEAD";
197
198 using (WebResponse response = request.GetResponse())
199 {
200 using (Stream responseStream = response.GetResponseStream())
201 {
202 // Create the metadata object
203 metadata = new AssetMetadata();
204 metadata.ContentType = response.ContentType;
205 metadata.ID = id;
206
207 UUID uuid;
208 if (UUID.TryParse(id, out uuid))
209 metadata.FullID = uuid;
210
211 string lastModifiedStr = response.Headers.Get("Last-Modified");
212 if (!String.IsNullOrEmpty(lastModifiedStr))
213 {
214 DateTime lastModified;
215 if (DateTime.TryParse(lastModifiedStr, out lastModified))
216 metadata.CreationDate = lastModified;
217 }
218 }
219 }
220 }
221 catch (Exception ex)
222 {
223 m_log.Warn("[ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message);
224 }
225
226 return metadata;
227 }
228
229 public byte[] GetData(string id)
230 {
231 AssetBase asset = Get(id);
232
233 if (asset != null)
234 return asset.Data;
235
236 return null;
237 }
238
239 /// <summary>
240 /// Get an asset asynchronously
241 /// </summary>
242 /// <param name="id">The asset id</param>
243 /// <param name="sender">Represents the requester. Passed back via the handler</param>
244 /// <param name="handler">The handler to call back once the asset has been retrieved</param>
245 /// <returns>True if the id was parseable, false otherwise</returns>
246 public bool Get(string id, Object sender, AssetRetrieved handler)
247 {
248 Util.FireAndForget(
249 delegate(object o)
250 {
251 AssetBase asset = Get(id);
252 handler(id, sender, asset);
253 }
254 );
255
256 return true;
257 }
258
259 /// <summary>
260 /// Creates a new asset
261 /// </summary>
262 /// Returns a random ID if none is passed into it
263 /// <param name="asset"></param>
264 /// <returns></returns>
265 public string Store(AssetBase asset)
266 {
267 bool storedInCache = false;
268 string errorMessage = null;
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 // Distinguish public and private assets
298 bool isPublic = true;
299 switch ((AssetType)asset.Type)
300 {
301 case AssetType.CallingCard:
302 case AssetType.Gesture:
303 case AssetType.LSLBytecode:
304 case AssetType.LSLText:
305 isPublic = false;
306 break;
307 }
308
309 // Make sure ContentType is set
310 if (String.IsNullOrEmpty(asset.Metadata.ContentType))
311 asset.Metadata.ContentType = SLUtil.SLAssetTypeToContentType(asset.Type);
312
313 // Build the remote storage request
314 List<MultipartForm.Element> postParameters = new List<MultipartForm.Element>()
315 {
316 new MultipartForm.Parameter("AssetID", asset.FullID.ToString()),
317 new MultipartForm.Parameter("CreatorID", asset.Metadata.CreatorID),
318 new MultipartForm.Parameter("Temporary", asset.Temporary ? "1" : "0"),
319 new MultipartForm.Parameter("Public", isPublic ? "1" : "0"),
320 new MultipartForm.File("Asset", asset.Name, asset.Metadata.ContentType, asset.Data)
321 };
322
323 // Make the remote storage request
324 try
325 {
326 HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_serverUrl);
327
328 HttpWebResponse response = MultipartForm.Post(request, postParameters);
329 using (Stream responseStream = response.GetResponseStream())
330 {
331 try
332 {
333 string responseStr = responseStream.GetStreamString();
334 OSD responseOSD = OSDParser.Deserialize(responseStr);
335 if (responseOSD.Type == OSDType.Map)
336 {
337 OSDMap responseMap = (OSDMap)responseOSD;
338 if (responseMap["Success"].AsBoolean())
339 return asset.ID;
340 else
341 errorMessage = "Upload failed: " + responseMap["Message"].AsString();
342 }
343 else
344 {
345 errorMessage = "Response format was invalid.";
346 }
347 }
348 catch
349 {
350 errorMessage = "Failed to parse the response.";
351 }
352 }
353 }
354 catch (WebException ex)
355 {
356 errorMessage = ex.Message;
357 }
358
359 m_log.WarnFormat("[ASSET CONNECTOR]: Failed to store asset \"{0}\" ({1}, {2}): {3}",
360 asset.Name, asset.ID, asset.Metadata.ContentType, errorMessage);
361 return null;
362 }
363
364 /// <summary>
365 /// Update an asset's content
366 /// </summary>
367 /// Attachments and bare scripts need this!!
368 /// <param name="id"> </param>
369 /// <param name="data"></param>
370 /// <returns></returns>
371 public bool UpdateContent(string id, byte[] data)
372 {
373 AssetBase asset = Get(id);
374
375 if (asset == null)
376 {
377 m_log.Warn("[ASSET CONNECTOR]: Failed to fetch asset " + id + " for updating");
378 return false;
379 }
380
381 asset.Data = data;
382
383 string result = Store(asset);
384 return !String.IsNullOrEmpty(result);
385 }
386
387 /// <summary>
388 /// Delete an asset
389 /// </summary>
390 /// <param name="id"></param>
391 /// <returns></returns>
392 public bool Delete(string id)
393 {
394 if (m_cache != null)
395 m_cache.Expire(id);
396
397 string url = m_serverUrl + id;
398
399 OSDMap response = WebUtil.ServiceRequest(url, "DELETE");
400 if (response["Success"].AsBoolean())
401 return true;
402 else
403 m_log.Warn("[ASSET CONNECTOR]: Failed to delete asset " + id + " from the asset service");
404
405 return false;
406 }
407
408 #endregion IAssetService
409 }
410}