staf4ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/staf4ruby.rb ADDED
@@ -0,0 +1,564 @@
1
+ #!/usr/bin/env ruby
2
+ ##############################################################################
3
+ # Copyright (c) 2013 Spectra Logic corp
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.eclipse.org/legal/epl-v10.html
8
+ ##############################################################################
9
+
10
+
11
+ $:.unshift File.dirname(File.expand_path(__FILE__))
12
+ require 'staf4ruby_ext'
13
+
14
+ # STAF bindings for Ruby
15
+
16
+
17
+ module Staf
18
+ class STAFException < StandardError
19
+ end
20
+
21
+ class STAFResult
22
+ Ok = 0
23
+ InvalidAPI = 1
24
+ UnknownService = 2
25
+ InvalidHandle = 3
26
+ HandleAlreadyExists = 4
27
+ HandleDoesNotExist = 5
28
+ UnknownError = 6
29
+ InvalidRequestString = 7
30
+ InvalidServiceResult = 8
31
+ REXXError = 9
32
+ BaseOSError = 10
33
+ ProcessAlreadyComplete = 11
34
+ ProcessNotComplete = 12
35
+ VariableDoesNotExist = 13
36
+ UnResolvableString = 14
37
+ InvalidResolveString = 15
38
+ NoPathToMachine = 16
39
+ FileOpenError = 17
40
+ FileReadError = 18
41
+ FileWriteError = 19
42
+ FileDeleteError = 20
43
+ STAFNotRunning = 21
44
+ CommunicationError = 22
45
+ TrusteeDoesNotExist = 23
46
+ InvalidTrustLevel = 24
47
+ AccessDenied = 25
48
+ STAFRegistrationError = 26
49
+ ServiceConfigurationError = 27
50
+ QueueFull = 28
51
+ NoQueueElement = 29
52
+ NotifieeDoesNotExist = 30
53
+ InvalidAPILevel = 31
54
+ ServiceNotUnregisterable = 32
55
+ ServiceNotAvailable = 33
56
+ SemaphoreDoesNotExist = 34
57
+ NotSemaphoreOwner = 35
58
+ SemaphoreHasPendingRequests = 36
59
+ Timeout = 37
60
+ JavaError = 38
61
+ ConverterError = 39
62
+ MoveError = 40
63
+ InvalidObject = 41
64
+ InvalidParm = 42
65
+ RequestNumberNotFound = 43
66
+ InvalidAsynchOption = 44
67
+ RequestNotComplete = 45
68
+ ProcessAuthenticationDenied = 46
69
+ InvalidValue = 47
70
+ DoesNotExist = 48
71
+ AlreadyExists = 49
72
+ DirectoryNotEmpty = 50
73
+ DirectoryCopyError = 51
74
+ DiagnosticsNotEnabled = 52
75
+ HandleAuthenticationDenied = 53
76
+ HandleAlreadyAuthenticated = 54
77
+ InvalidSTAFVersion = 55
78
+ RequestCancelled = 56
79
+ CreateThreadError = 57
80
+ MaximumSizeExceeded = 58
81
+ MaximumHandlesExceeded = 59
82
+ NotRequester = 60
83
+
84
+ attr_reader :rc, :result, :resultContext, :resultObj
85
+
86
+ def initialize(rc, result, doUnmarshallResult)
87
+ @rc = rc
88
+ @result = result
89
+ if doUnmarshallResult
90
+ @resultContext = Staf::unmarshall(@result)
91
+ @resultObj = @resultContext.rootObject
92
+ else
93
+ @resultContext = nil
94
+ @resultObj = nil
95
+ end
96
+ end
97
+ end
98
+
99
+ class STAFHandle
100
+ # STAFHandle types
101
+
102
+ Standard = 0
103
+ Static = 1
104
+
105
+ # Modes for submit call
106
+
107
+ Synchronous = 0
108
+ FireAndForget = 1
109
+ Queue = 2
110
+ Retain = 3
111
+ QueueRetain = 4
112
+
113
+ attr_reader :doUnmarshallResult
114
+ attr_reader :handleType
115
+
116
+ def initialize(handleNameOrNumber, handleType = Standard)
117
+ @handleType = handleType
118
+ @doUnmarshallResult = true
119
+
120
+ if (handleType == Standard)
121
+ if (not handleNameOrNumber.is_a? String)
122
+ raise TypeError.new('A string is required if using standard handle type')
123
+ end
124
+ rc, @handle = Staf::register(handleNameOrNumber)
125
+ if rc != STAFResult::Ok
126
+ raise STAFException.new(rc)
127
+ end
128
+ else
129
+ if (not handleNameOrNumber.is_a? Integer)
130
+ raise TypeError.new('An integer is required if using static handle type')
131
+ end
132
+ @handle = handleNameOrNumber
133
+ end
134
+ end
135
+
136
+ # Set the doUnmarshallResult attribute. For compatibility with the Python
137
+ # bindings, accept other-than-boolean values and check their truthiness.
138
+ def setDoUnmarshallResult(value)
139
+ if value.is_a? Fixnum
140
+ @doUnmarshallResult = (value != 0)
141
+ elsif value.is_a? String
142
+ @doUnmarshallResult = (value != "")
143
+ else
144
+ @doUnmarshallResult = value
145
+ end
146
+ end
147
+
148
+ def submit(location, service, request, mode = Synchronous)
149
+ rc, result = Staf::submit(@handle, mode, location, service, request)
150
+ STAFResult.new(rc, result, @doUnmarshallResult)
151
+ end
152
+
153
+ def unregister()
154
+ if (@handleType == Standard)
155
+ rc = Staf::unregister(@handle)
156
+ if (rc != STAFResult::Ok)
157
+ raise STAFException.new(rc)
158
+ end
159
+ @handle = 0
160
+ end
161
+ 0
162
+ end
163
+
164
+ end
165
+
166
+ # Marshalling constants
167
+ UNMARSHALLING_DEFAULTS = 0
168
+ IGNORE_INDIRECT_OBJECTS = 1
169
+ MARSHALLED_DATA_MARKER = '@SDT/'
170
+ NONE_MARKER = '@SDT/$0:0:'
171
+ SCALAR_MARKER = '@SDT/$'
172
+ SCALAR_MARKER_REGEX = '@SDT/\$' # Version of LIST_MARKER for regexes
173
+ SCALAR_STRING_MARKER = '@SDT/$S'
174
+ LIST_MARKER = '@SDT/['
175
+ LIST_MARKER_REGEX = '@SDT/\[' # Version of LIST_MARKER for regexes
176
+ MAP_MARKER = '@SDT/{'
177
+ MC_INSTANCE_MARKER = '@SDT/%'
178
+ CONTEXT_MARKER = '@SDT/*'
179
+ CONTEXT_MARKER_REGEX = '@SDT/\*'
180
+
181
+
182
+ class STAFMapClassDefinition
183
+ attr_reader :mapClassDef
184
+
185
+ def initialize(name = nil, mapClassDef = nil)
186
+ # @mapClassDef[keys] is an array of key=>value pairs. It would seem to
187
+ # be more useful to just make it a hash of key=>value pairs, but this is
188
+ # how the python bindings do it. It's not completely equivalent, because
189
+ # this way preserves order and allows for duplicate keys.
190
+ if mapClassDef.nil? and name.nil?
191
+ @mapClassDef = {"name" => "", "keys" => []}
192
+ elsif not name.nil?
193
+ @mapClassDef = {"name" => name, 'keys' => []}
194
+ else
195
+ @mapClassDef = mapClassDef
196
+ end
197
+ end
198
+
199
+ def createInstance
200
+ {"staf-map-class-name" => @mapClassDef["name"]}
201
+ end
202
+
203
+ def addKey(keyName, displayName = nil)
204
+ theKey = {"key" => keyName}
205
+ if displayName
206
+ theKey['display-name'] = displayName
207
+ end
208
+ @mapClassDef["keys"] << theKey
209
+ end
210
+
211
+ def setKeyProperty(keyName, property, value)
212
+ @mapClassDef['keys'].each do |key|
213
+ if key['key'] == keyName
214
+ key[property] = value
215
+ end
216
+ end
217
+ end
218
+
219
+ def keys
220
+ @mapClassDef["keys"]
221
+ end
222
+
223
+ def name
224
+ @mapClassDef["name"]
225
+ end
226
+
227
+ # def to_s
228
+ # formatObject @mapClassDef
229
+ # end
230
+ #
231
+ # def inspect
232
+ # to_s
233
+ # end
234
+
235
+ end
236
+
237
+ class STAFMarshallingContext
238
+ attr_accessor :rootObject
239
+ attr_reader :mapClassMap
240
+
241
+ def initialize(obj=nil, mapClassMap=nil)
242
+ if mapClassMap.nil?
243
+ @mapClassMap = {}
244
+ else
245
+ @mapClassMap = mapClassMap
246
+ end
247
+ @rootObject = obj
248
+ end
249
+
250
+ def isMarshalledData(someData)
251
+ Staf::isMarshalledData(someData)
252
+ end
253
+
254
+ def getMapClassDefinition(mapClassName)
255
+ STAFMapClassDefinition.new(nil, @mapClassMap[mapClassName])
256
+ end
257
+
258
+ def getPrimaryObject
259
+ if @mapClassMap.size == 0
260
+ @rootObject
261
+ else
262
+ self
263
+ end
264
+ end
265
+
266
+ def hasMapClassDefinition(mapClassName)
267
+ @mapClassMap.keys.include? mapClassName
268
+ end
269
+
270
+ def marshall
271
+ Staf::marshall(self, self)
272
+ end
273
+
274
+ def mapClassDefinitionIterator
275
+ @mapClassMap.keys
276
+ end
277
+
278
+ def setMapClassDefinition(mapClassDef)
279
+ @mapClassMap[mapClassDef.name()] = mapClassDef.mapClassDef
280
+ end
281
+
282
+ # def to_s
283
+ # formatObject(@rootObject, self)
284
+ # end
285
+
286
+ # def inspect
287
+ # to_s
288
+ # end
289
+ end
290
+
291
+ def self.isMarshalledData(someData)
292
+ someData =~ /\A@SDT\//
293
+ end
294
+
295
+ def self.marshall(object, context=nil)
296
+ # General Marshalling function
297
+
298
+ return NONE_MARKER if object.nil?
299
+ if object.is_a? Array
300
+ listData = object.collect do |item|
301
+ marshall(item, context)
302
+ end.join
303
+ return "#{LIST_MARKER}#{object.size}:#{listData.size}:#{listData}"
304
+ elsif object.is_a? Hash
305
+ # If a staf-map-class-name key exists in the map, make sure that
306
+ # it's map class definition is provided in the marshalling context.
307
+ # If it's not, then treat the map as a plain map object.
308
+ isMapClass = false
309
+ mapClassName = ""
310
+ if not context.nil? and context.is_a? STAFMarshallingContext and \
311
+ object.has_key?('staf-map-class-name')
312
+ mapClassName = object['staf-map-class-name']
313
+ if context.hasMapClassDefinition(mapClassName)
314
+ isMapClass = true
315
+ end
316
+ end
317
+
318
+ if isMapClass
319
+ mapClass = context.getMapClassDefinition(mapClassName)
320
+ mapData = ":#{mapClassName.size}:#{mapClassName}"
321
+ mapData << mapClass.keys.collect do |key|
322
+ marshall(object[key['key']], context)
323
+ end.join
324
+ return "#{MC_INSTANCE_MARKER}:#{mapData.size}:#{mapData}"
325
+ else
326
+ mapData = object.keys.collect do |key|
327
+ ":#{key.to_s.size}:#{key.to_s}#{marshall(object[key], context)}"
328
+ end.join
329
+ return "#{MAP_MARKER}:#{mapData.size}:#{mapData}"
330
+ end
331
+ elsif object.is_a? STAFMarshallingContext
332
+ if object.mapClassMap.keys.size == 0
333
+ return marshall(object.rootObject, context)
334
+ else
335
+ contextMap = { 'map-class-map' => object.mapClassMap}
336
+ # Note: We can't simply put the root object as a map key like
337
+ # "root-object" and then marshall the whole map, as in
338
+ # the unmarshalling routines, we need to be able to
339
+ # unmarshall the root object in the context of the
340
+ # map-class-map.
341
+ mcData = (marshall(contextMap, context) +
342
+ marshall(object.rootObject, object))
343
+ return "#{CONTEXT_MARKER}:#{mcData.size}:#{mcData}"
344
+ end
345
+ end
346
+
347
+ return "#{SCALAR_STRING_MARKER}:#{object.to_s.size}:#{object.to_s}"
348
+ end
349
+
350
+
351
+ # General unmarshalling function
352
+ # Unmarshalls the input data string and returns a marshalling context.
353
+ # Unlike the Python version, this function does not catch exceptions
354
+
355
+ def self.unmarshall(data, context = nil, flags = UNMARSHALLING_DEFAULTS)
356
+ # TODO: implement me
357
+ if context.nil?
358
+ context = STAFMarshallingContext.new
359
+ end
360
+
361
+ if data =~ /\A#{NONE_MARKER}/
362
+ return STAFMarshallingContext.new
363
+ elsif data =~ /\A#{SCALAR_MARKER_REGEX}/
364
+ # @SDT/$S:<string length>:<String>
365
+ colonIndex = data.index(":", SCALAR_MARKER.size)
366
+ return STAFMarshallingContext.new(data) unless colonIndex
367
+ dataIndex = colonIndex + 1
368
+ colonIndex = data.index(':', dataIndex)
369
+ return STAFMarshallingContext.new(data) unless colonIndex
370
+ stringLength = data[dataIndex..colonIndex - 1].to_i
371
+ dataIndex = colonIndex + 1
372
+ if stringLength != (data.size - dataIndex)
373
+ return STAFMarshallingContext.new(data)
374
+ end
375
+ theString = data[dataIndex..-1]
376
+ if (theString =~ /\A#{MARSHALLED_DATA_MARKER}/) and \
377
+ ((flags & IGNORE_INDIRECT_OBJECTS) != IGNORE_INDIRECT_OBJECTS)
378
+ return unmarshall(theString, context, flags)
379
+ else
380
+ return STAFMarshallingContext.new(theString)
381
+ end
382
+ elsif data =~ /\A#{LIST_MARKER_REGEX}/
383
+ # @SDT/[<number-of-items>:<array-length>:<SDT-Any-1>...<SDT-Any-n>
384
+ # Get number-of-items in the list
385
+ colonIndex = data.index(":", LIST_MARKER.size)
386
+ return STAFMarshallingContext.new(data) unless colonIndex
387
+ numItems = data[LIST_MARKER.size..colonIndex - 1].to_i
388
+ # Get array length
389
+ dataIndex = colonIndex + 1
390
+ colonIndex = data.index(":", dataIndex)
391
+ return STAFMarshallingContext.new(data) unless colonIndex
392
+ arrayLength = data[dataIndex..colonIndex-1].to_i
393
+ dataIndex = colonIndex + 1
394
+ if arrayLength != data.size - dataIndex
395
+ return STAFMarshallingContext.new(data)
396
+ end
397
+
398
+ # Create an array for the data
399
+ myArray = []
400
+ for i in (1..numItems).to_a
401
+ # Get the next item in the list and unmarshall it and add it
402
+ # to the list
403
+ colonIndex1 = data.index(":", dataIndex)
404
+ return STAFMarshallingContext.new(data) unless colonIndex1
405
+ colonIndex2 = data.index(":", colonIndex1 + 1)
406
+ return STAFMarshallingContext.new(data) unless colonIndex2
407
+ itemLength = data[colonIndex1 + 1..colonIndex2 - 1].to_i
408
+ myArray << (unmarshall(data[dataIndex..colonIndex2 + itemLength],
409
+ context, flags).getPrimaryObject)
410
+ dataIndex = colonIndex2 + itemLength + 1
411
+ end
412
+ return STAFMarshallingContext.new(myArray)
413
+ elsif data =~ /\A#{MAP_MARKER}/
414
+ # @SDT/{:<map-length>::<key-1-length>:<key-1><SDT-Any>
415
+ # ...
416
+ # :<key-n-length>:<key-1><SDT-Any>
417
+
418
+ # Get map-length
419
+ colonIndex = data.index(":", MAP_MARKER.size)
420
+ return STAFMarshallingContext.new(data) unless colonIndex
421
+ dataIndex = colonIndex + 1
422
+ colonIndex = data.index(":", dataIndex)
423
+ return STAFMarshallingContext.new(data) unless colonIndex
424
+ mapLength = data[dataIndex..colonIndex].to_i
425
+ dataIndex = colonIndex + 1
426
+ if mapLength != data.size - dataIndex
427
+ return STAFMarshallingContext.new(data)
428
+ end
429
+
430
+ # Create the hash of data
431
+ myhash = {}
432
+ while dataIndex < data.size
433
+ # Get the key first
434
+ keyColonIndex1 = data.index(":", dataIndex)
435
+ return STAFMarshallingContext.new(data) unless keyColonIndex1
436
+ keyColonIndex2 = data.index(":", keyColonIndex1 + 1)
437
+ return STAFMarshallingContext.new(data) unless keyColonIndex2
438
+ keyLength = data[keyColonIndex1 + 1..keyColonIndex2 - 1].to_i
439
+ key = data[keyColonIndex2 + 1..keyColonIndex2 + keyLength]
440
+ dataIndex = keyColonIndex2 + 1 + keyLength
441
+
442
+ # Now, get the object
443
+ colonIndex1 = data.index(":", dataIndex)
444
+ return STAFMarshallingContext.new(data) unless colonIndex1
445
+ colonIndex2 = data.index(":", colonIndex1 + 1)
446
+ return STAFMarshallingcontext.new(data) unless colonIndex2
447
+ itemLength = data[colonIndex1 + 1..colonIndex2-1].to_i
448
+ myhash[key] = unmarshall(data[dataIndex..colonIndex2 + itemLength],
449
+ context, flags).getPrimaryObject
450
+ dataIndex = colonIndex2 + itemLength + 1
451
+ end
452
+ return STAFMarshallingContext.new(myhash)
453
+ elsif data =~ /\A#{MC_INSTANCE_MARKER}/
454
+ # @SDT/%:<map-class-instance-length>::<map-class-name-length>:
455
+ # <map-class-name><SDT-Any-value-1>...<SDT-Any-value-n>
456
+
457
+ # Get the map-class-instance-length
458
+ colonIndex = data.index(":", MC_INSTANCE_MARKER.size)
459
+ return STAFMarshallingContext.new(data) unless colonIndex
460
+ dataIndex = colonIndex + 1
461
+ colonIndex = data.index(":", dataIndex)
462
+ return STAFMarshallingContext.new(data) unless colonIndex
463
+ mapClassInstanceLength = data[dataIndex .. colonIndex - 1].to_i
464
+ dataIndex = colonIndex + 1
465
+ if mapClassInstanceLength != data.size - dataIndex
466
+ return STAFMarshallingContext.new(data)
467
+ end
468
+
469
+ # get map-class-name-length
470
+ colonIndex = data.index(":", dataIndex)
471
+ return STAFMarshallingContext.new(data) unless colonIndex
472
+ dataIndex = colonIndex + 1
473
+ colonIndex = data.index(":", dataIndex)
474
+ return STAFMarshallingContext.new(data) unless colonIndex
475
+ mapClassNameLength = data[dataIndex .. colonIndex - 1].to_i
476
+
477
+ # Get map-class-name
478
+ dataIndex = colonIndex + 1
479
+ mapClassName = data[dataIndex .. dataIndex + mapClassNameLength - 1]
480
+ dataIndex = dataIndex + mapClassNameLength
481
+
482
+ # Create a hash and add the staf-map-class-name key and value to the hash
483
+ myHash = {'staf-map-class-name' => mapClassName}
484
+
485
+ # Unmarshall all of the actual keys and add to the map
486
+ mapClass = context.getMapClassDefinition(mapClassName)
487
+ keys = mapClass.keys
488
+ keyIndex = 0
489
+
490
+ while dataIndex < data.size
491
+ colonIndex = data.index(":", dataIndex)
492
+ return STAFMarshallingContext.new(data) unless colonIndex
493
+ colonIndex2 = data.index(":", colonIndex + 1)
494
+ return STAFMarshallingContext.new(data) unless colonIndex2
495
+ itemLength = data[colonIndex + 1 .. colonIndex2].to_i
496
+ myHash[keys[keyIndex]['key']] = unmarshall(
497
+ data[dataIndex .. colonIndex2 + itemLength],
498
+ context, flags).getPrimaryObject
499
+ dataIndex = colonIndex2 + itemLength + 1
500
+ keyIndex += 1
501
+ end
502
+
503
+ return STAFMarshallingContext.new(myHash)
504
+
505
+ elsif data =~ /\A#{CONTEXT_MARKER_REGEX}/
506
+ # @SDT/*:<context-length>:
507
+ # @SDT/{:<mapClassLength>:<mapClassData><rootObject>
508
+
509
+ # Get context-length
510
+ colonIndex = data.index(":", CONTEXT_MARKER.size)
511
+ return STAFMarshallingContext.new(data) unless colonIndex
512
+ contextIndex = data.index(":", colonIndex + 1)
513
+ return STAFMarshallingContext.new(data) unless contextIndex
514
+ contextLength = data[colonIndex + 1 .. contextIndex - 1].to_i
515
+ contextIndex += 1
516
+ if contextLength != data.size - contextIndex
517
+ return STAFMarshallingContext.new(data)
518
+ end
519
+
520
+ # Get mapClassLength
521
+ colonIndex = data.index(":", contextIndex)
522
+ return STAFMarshallingContext.new(data) unless colonIndex
523
+ mapIndex = contextIndex
524
+ mapDataIndex = data.index(":", colonIndex + 1)
525
+ return STAFMarshallingContext.new(data) unless mapDataIndex
526
+ mapLength = data[colonIndex + 1 .. mapDataIndex - 1].to_i
527
+ mapDataIndex += 1
528
+
529
+ if mapLength > data.size - mapDataIndex
530
+ return STAFMarshallingContext.new(data)
531
+ end
532
+
533
+ # Create a new marshalling context with the map classes and root object
534
+ contextMap = unmarshall(data[mapIndex..mapDataIndex + mapLength - 1],
535
+ context, flags).getPrimaryObject()
536
+ mapClassMap = contextMap['map-class-map']
537
+ newContext = STAFMarshallingContext.new(nil, mapClassMap)
538
+ colonIndex = data.index(":", mapDataIndex + mapLength)
539
+ return STAFMarshallingContext.new(data) unless colonIndex
540
+ rootObjIndex = mapDataIndex + mapLength
541
+ rootObjDataIndex = data.index(":", colonIndex + 1)
542
+ return STAFMarshallingContext.new(data) unless rootObjDataIndex
543
+
544
+ rootObjLength = data[colonIndex + 1 .. rootObjDataIndex - 1].to_i
545
+ rootObjDataIndex = rootObjDataIndex + 1
546
+ if rootObjLength > data.size - rootObjDataIndex
547
+ return STAFMarshallingContext.new(data)
548
+ end
549
+
550
+ newContext.rootObject = \
551
+ unmarshall(data[rootObjIndex .. rootObjDataIndex + rootObjLength - 1],
552
+ newContext, flags).getPrimaryObject()
553
+
554
+ return newContext
555
+ elsif data =~ /\A#{MARSHALLED_DATA_MARKER}/
556
+ return STAFMarshallingContext.new(data)
557
+ end
558
+
559
+ # Shouldn't get here
560
+ STAFMarshallingContext.new(data)
561
+ end
562
+
563
+
564
+ end