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