aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs
diff options
context:
space:
mode:
authorMic Bowman2012-04-17 13:45:27 -0700
committerMic Bowman2012-04-17 13:45:27 -0700
commit5ff2bda587a50b73f470ba778348645953b6b4b0 (patch)
treeef2164de4447ca08b96f7e603b74763ea72ca2b0 /OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs
parentmake the namespace for the ScriptModuleComms consistent with its file system ... (diff)
downloadopensim-SC_OLD-5ff2bda587a50b73f470ba778348645953b6b4b0.zip
opensim-SC_OLD-5ff2bda587a50b73f470ba778348645953b6b4b0.tar.gz
opensim-SC_OLD-5ff2bda587a50b73f470ba778348645953b6b4b0.tar.bz2
opensim-SC_OLD-5ff2bda587a50b73f470ba778348645953b6b4b0.tar.xz
This commit adds a new optional region module, JsonStore, that provides structured
storage (dictionaries and arrays of string values) for scripts and region modules. In addition, there are operations on the storage that enable "real" distributed computation between scripts through operations similar to those of a tuple space. Scripts can share task queues, implement shared locks or semaphores, etc. The structured store is limited to the current region and is not currently persisted. However, script operations are defined to initialize a store from a notecard and to serialize the store to a notecard. Documentation will be posted to the opensim wiki soon.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs498
1 files changed, 498 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs
new file mode 100644
index 0000000..49556b6
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs
@@ -0,0 +1,498 @@
1/*
2 * Copyright (c) Contributors
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 OpenSim 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 */
27using Mono.Addins;
28
29using System;
30using System.Reflection;
31using System.Threading;
32using System.Text;
33using System.Net;
34using System.Net.Sockets;
35using log4net;
36using Nini.Config;
37using OpenMetaverse;
38using OpenMetaverse.StructuredData;
39using OpenSim.Framework;
40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes;
42using System.Collections.Generic;
43using System.Text.RegularExpressions;
44
45namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
46{
47 public class JsonStore
48 {
49 private static readonly ILog m_log =
50 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51
52 private OSD m_ValueStore;
53
54 protected class TakeValueCallbackClass
55 {
56 public string Path { get; set; }
57 public bool UseJson { get; set; }
58 public TakeValueCallback Callback { get; set; }
59
60 public TakeValueCallbackClass(string spath, bool usejson, TakeValueCallback cback)
61 {
62 Path = spath;
63 UseJson = usejson;
64 Callback = cback;
65 }
66 }
67
68 protected List<TakeValueCallbackClass> m_TakeStore;
69 protected List<TakeValueCallbackClass> m_ReadStore;
70
71
72 // -----------------------------------------------------------------
73 /// <summary>
74 ///
75 /// </summary>
76 // -----------------------------------------------------------------
77 public JsonStore(string value = "")
78 {
79 m_TakeStore = new List<TakeValueCallbackClass>();
80 m_ReadStore = new List<TakeValueCallbackClass>();
81
82 if (String.IsNullOrEmpty(value))
83 m_ValueStore = new OSDMap();
84 else
85 m_ValueStore = OSDParser.DeserializeJson(value);
86 }
87
88 // -----------------------------------------------------------------
89 /// <summary>
90 ///
91 /// </summary>
92 // -----------------------------------------------------------------
93 public bool TestPath(string expr, bool useJson)
94 {
95 Stack<string> path = ParsePathExpression(expr);
96 OSD result = ProcessPathExpression(m_ValueStore,path);
97
98 if (result == null)
99 return false;
100
101 if (useJson || result.Type == OSDType.String)
102 return true;
103
104 return false;
105 }
106
107 // -----------------------------------------------------------------
108 /// <summary>
109 ///
110 /// </summary>
111 // -----------------------------------------------------------------
112 public bool GetValue(string expr, out string value, bool useJson)
113 {
114 Stack<string> path = ParsePathExpression(expr);
115 OSD result = ProcessPathExpression(m_ValueStore,path);
116 return ConvertOutputValue(result,out value,useJson);
117 }
118
119
120 // -----------------------------------------------------------------
121 /// <summary>
122 ///
123 /// </summary>
124 // -----------------------------------------------------------------
125 public bool RemoveValue(string expr)
126 {
127 return SetValueFromExpression(expr,null);
128 }
129
130 // -----------------------------------------------------------------
131 /// <summary>
132 ///
133 /// </summary>
134 // -----------------------------------------------------------------
135 public bool SetValue(string expr, string value, bool useJson)
136 {
137 OSD ovalue = useJson ? OSDParser.DeserializeJson(value) : new OSDString(value);
138 return SetValueFromExpression(expr,ovalue);
139 }
140
141 // -----------------------------------------------------------------
142 /// <summary>
143 ///
144 /// </summary>
145 // -----------------------------------------------------------------
146 public bool TakeValue(string expr, bool useJson, TakeValueCallback cback)
147 {
148 Stack<string> path = ParsePathExpression(expr);
149 string pexpr = PathExpressionToKey(path);
150
151 OSD result = ProcessPathExpression(m_ValueStore,path);
152 if (result == null)
153 {
154 m_TakeStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
155 return false;
156 }
157
158 string value = String.Empty;
159 if (! ConvertOutputValue(result,out value,useJson))
160 {
161 // the structure does not match the request so i guess we'll wait
162 m_TakeStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
163 return false;
164 }
165
166 SetValueFromExpression(expr,null);
167 cback(value);
168
169 return true;
170 }
171
172 // -----------------------------------------------------------------
173 /// <summary>
174 ///
175 /// </summary>
176 // -----------------------------------------------------------------
177 public bool ReadValue(string expr, bool useJson, TakeValueCallback cback)
178 {
179 Stack<string> path = ParsePathExpression(expr);
180 string pexpr = PathExpressionToKey(path);
181
182 OSD result = ProcessPathExpression(m_ValueStore,path);
183 if (result == null)
184 {
185 m_ReadStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
186 return false;
187 }
188
189 string value = String.Empty;
190 if (! ConvertOutputValue(result,out value,useJson))
191 {
192 // the structure does not match the request so i guess we'll wait
193 m_ReadStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
194 return false;
195 }
196
197 cback(value);
198
199 return true;
200 }
201
202 // -----------------------------------------------------------------
203 /// <summary>
204 ///
205 /// </summary>
206 // -----------------------------------------------------------------
207 protected bool SetValueFromExpression(string expr, OSD ovalue)
208 {
209 Stack<string> path = ParsePathExpression(expr);
210 if (path.Count == 0)
211 {
212 m_ValueStore = ovalue;
213 return true;
214 }
215
216 string pkey = path.Pop();
217 string pexpr = PathExpressionToKey(path);
218 if (pexpr != "")
219 pexpr += ".";
220
221 OSD result = ProcessPathExpression(m_ValueStore,path);
222 if (result == null)
223 return false;
224
225 Regex aPattern = new Regex("\\[([0-9]+|\\+)\\]");
226 MatchCollection amatches = aPattern.Matches(pkey,0);
227
228 if (amatches.Count > 0)
229 {
230 if (result.Type != OSDType.Array)
231 return false;
232
233 OSDArray amap = result as OSDArray;
234
235 Match match = amatches[0];
236 GroupCollection groups = match.Groups;
237 string akey = groups[1].Value;
238
239 if (akey == "+")
240 {
241 string npkey = String.Format("[{0}]",amap.Count);
242
243 amap.Add(ovalue);
244 InvokeNextCallback(pexpr + npkey);
245 return true;
246 }
247
248 int aval = Convert.ToInt32(akey);
249 if (0 <= aval && aval < amap.Count)
250 {
251 if (ovalue == null)
252 amap.RemoveAt(aval);
253 else
254 {
255 amap[aval] = ovalue;
256 InvokeNextCallback(pexpr + pkey);
257 }
258 return true;
259 }
260
261 return false;
262 }
263
264 Regex hPattern = new Regex("{([^}]+)}");
265 MatchCollection hmatches = hPattern.Matches(pkey,0);
266
267 if (hmatches.Count > 0)
268 {
269 Match match = hmatches[0];
270 GroupCollection groups = match.Groups;
271 string hkey = groups[1].Value;
272
273 if (result is OSDMap)
274 {
275 OSDMap hmap = result as OSDMap;
276 if (ovalue != null)
277 {
278 hmap[hkey] = ovalue;
279 InvokeNextCallback(pexpr + pkey);
280 }
281 else if (hmap.ContainsKey(hkey))
282 hmap.Remove(hkey);
283
284 return true;
285 }
286
287 return false;
288 }
289
290 // Shouldn't get here if the path was checked correctly
291 m_log.WarnFormat("[JsonStore] invalid path expression");
292 return false;
293 }
294
295 // -----------------------------------------------------------------
296 /// <summary>
297 ///
298 /// </summary>
299 // -----------------------------------------------------------------
300 protected bool InvokeNextCallback(string pexpr)
301 {
302 // Process all of the reads that match the expression first
303 List<TakeValueCallbackClass> reads =
304 m_ReadStore.FindAll(delegate(TakeValueCallbackClass tb) { return pexpr.StartsWith(tb.Path); });
305
306 foreach (TakeValueCallbackClass readcb in reads)
307 {
308 m_ReadStore.Remove(readcb);
309 ReadValue(readcb.Path,readcb.UseJson,readcb.Callback);
310 }
311
312 // Process one take next
313 TakeValueCallbackClass takecb =
314 m_TakeStore.Find(delegate(TakeValueCallbackClass tb) { return pexpr.StartsWith(tb.Path); });
315
316 if (takecb != null)
317 {
318 m_TakeStore.Remove(takecb);
319 TakeValue(takecb.Path,takecb.UseJson,takecb.Callback);
320
321 return true;
322 }
323
324 return false;
325 }
326
327 // -----------------------------------------------------------------
328 /// <summary>
329 /// Parse the path expression and put the components into a stack. We
330 /// use a stack because we process the path in inverse order later
331 /// </summary>
332 // -----------------------------------------------------------------
333 protected static Stack<string> ParsePathExpression(string path)
334 {
335 Stack<string> m_path = new Stack<string>();
336
337 // add front and rear separators
338 path = "." + path + ".";
339
340 // add separators for quoted paths
341 Regex pass1 = new Regex("{[^}]+}");
342 path = pass1.Replace(path,".$0.",-1,0);
343
344 // add separators for array references
345 Regex pass2 = new Regex("(\\[[0-9]+\\]|\\[\\+\\])");
346 path = pass2.Replace(path,".$0.",-1,0);
347
348 // add quotes to bare identifier
349 Regex pass3 = new Regex("\\.([a-zA-Z]+)");
350 path = pass3.Replace(path,".{$1}",-1,0);
351
352 // remove extra separators
353 Regex pass4 = new Regex("\\.+");
354 path = pass4.Replace(path,".",-1,0);
355
356 Regex validate = new Regex("^\\.(({[^}]+}|\\[[0-9]+\\]|\\[\\+\\])\\.)+$");
357 if (validate.IsMatch(path))
358 {
359 Regex parser = new Regex("\\.({[^}]+}|\\[[0-9]+\\]|\\[\\+\\]+)");
360 MatchCollection matches = parser.Matches(path,0);
361 foreach (Match match in matches)
362 m_path.Push(match.Groups[1].Value);
363 }
364
365 return m_path;
366 }
367
368 // -----------------------------------------------------------------
369 /// <summary>
370 ///
371 /// </summary>
372 /// <param>path is a stack where the top level of the path is at the bottom of the stack</param>
373 // -----------------------------------------------------------------
374 protected static OSD ProcessPathExpression(OSD map, Stack<string> path)
375 {
376 if (path.Count == 0)
377 return map;
378
379 string pkey = path.Pop();
380
381 OSD rmap = ProcessPathExpression(map,path);
382 if (rmap == null)
383 return null;
384
385 // ---------- Check for an array index ----------
386 Regex aPattern = new Regex("\\[([0-9]+)\\]");
387 MatchCollection amatches = aPattern.Matches(pkey,0);
388
389 if (amatches.Count > 0)
390 {
391 if (rmap.Type != OSDType.Array)
392 {
393 m_log.WarnFormat("[JsonStore] wrong type for key {2}, expecting {0}, got {1}",OSDType.Array,rmap.Type,pkey);
394 return null;
395 }
396
397 OSDArray amap = rmap as OSDArray;
398
399 Match match = amatches[0];
400 GroupCollection groups = match.Groups;
401 string akey = groups[1].Value;
402 int aval = Convert.ToInt32(akey);
403
404 if (aval < amap.Count)
405 return (OSD) amap[aval];
406
407 return null;
408 }
409
410 // ---------- Check for a hash index ----------
411 Regex hPattern = new Regex("{([^}]+)}");
412 MatchCollection hmatches = hPattern.Matches(pkey,0);
413
414 if (hmatches.Count > 0)
415 {
416 if (rmap.Type != OSDType.Map)
417 {
418 m_log.WarnFormat("[JsonStore] wrong type for key {2}, expecting {0}, got {1}",OSDType.Map,rmap.Type,pkey);
419 return null;
420 }
421
422 OSDMap hmap = rmap as OSDMap;
423
424 Match match = hmatches[0];
425 GroupCollection groups = match.Groups;
426 string hkey = groups[1].Value;
427
428 if (hmap.ContainsKey(hkey))
429 return (OSD) hmap[hkey];
430
431 return null;
432 }
433
434 // Shouldn't get here if the path was checked correctly
435 m_log.WarnFormat("[JsonStore] Path type (unknown) does not match the structure");
436 return null;
437 }
438
439 // -----------------------------------------------------------------
440 /// <summary>
441 ///
442 /// </summary>
443 // -----------------------------------------------------------------
444 protected static bool ConvertOutputValue(OSD result, out string value, bool useJson)
445 {
446 value = String.Empty;
447
448 // If we couldn't process the path
449 if (result == null)
450 return false;
451
452 if (useJson)
453 {
454 // The path pointed to an intermediate hash structure
455 if (result.Type == OSDType.Map)
456 {
457 value = OSDParser.SerializeJsonString(result as OSDMap);
458 return true;
459 }
460
461 // The path pointed to an intermediate hash structure
462 if (result.Type == OSDType.Array)
463 {
464 value = OSDParser.SerializeJsonString(result as OSDArray);
465 return true;
466 }
467
468 value = "'" + result.AsString() + "'";
469 return true;
470 }
471
472 if (result.Type == OSDType.String)
473 {
474 value = result.AsString();
475 return true;
476 }
477
478 return false;
479 }
480
481 // -----------------------------------------------------------------
482 /// <summary>
483 ///
484 /// </summary>
485 // -----------------------------------------------------------------
486 protected static string PathExpressionToKey(Stack<string> path)
487 {
488 if (path.Count == 0)
489 return "";
490
491 string pkey = "";
492 foreach (string k in path)
493 pkey = (pkey == "") ? k : (k + "." + pkey);
494
495 return pkey;
496 }
497 }
498}