xamplr 1.9.0 → 1.9.1

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.
@@ -1,763 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'sync'
4
-
5
- module Xampl
6
-
7
- @@persister = nil
8
- @@known_persisters = {}
9
- @@persister_kinds = {}
10
-
11
- def Xampl.persister
12
- @@persister
13
- end
14
-
15
- def Xampl.block_future_changes(on=true)
16
- if(@@persister) then
17
- @@persister.block_changes = on
18
- end
19
- end
20
-
21
- def Xampl.auto_persistence(on=true)
22
- if(@@persister) then
23
- @@persister.automatic = on
24
- end
25
- end
26
-
27
- def Xampl.register_persister_kind(klass)
28
- @@persister_kinds[klass.kind] = klass
29
- end
30
-
31
- def Xampl.disable_all_persisters
32
- @@persister = nil
33
- @@known_persisters = {}
34
- end
35
-
36
- def Xampl.disable_persister
37
- @@persister = nil
38
- end
39
-
40
- @@default_persister_kind = :simple
41
- @@default_persister_format = :xml_format
42
-
43
- def Xampl.default_persister_kind
44
- @@default_persister_kind
45
- end
46
- def Xampl.set_default_persister_kind(kind)
47
- @@default_persister_kind = kind
48
- puts "SET KIND format: #{@@default_persister_format}, kind: #{@@default_persister_kind}"
49
- end
50
-
51
- def Xampl.default_persister_format
52
- @@default_persister_format
53
- end
54
- def Xampl.set_default_persister_format(format)
55
- @@default_persister_format = format
56
- puts "SET FORMAT format: #{@@default_persister_format}, kind: #{@@default_persister_kind}"
57
- end
58
-
59
- def Xampl.enable_persister(name, kind=nil, format=nil)
60
-
61
- kind = kind || @@default_persister_kind
62
- format = format || @@default_persister_format
63
- @@persister = @@known_persisters[name]
64
-
65
- if @@persister then
66
- #raise XamplException.new(:live_across_rollback) if @@persister.rolled_back
67
- if kind and kind != @@persister.kind then
68
- raise IncompatiblePersisterRequest.new(@@persister, "kind", kind, @@persister.kind)
69
- end
70
- if format and format != @@persister.format then
71
- raise IncompatiblePersisterRequest.new(@@persister, "format", format, @@persister.format)
72
- end
73
- end
74
-
75
- unless @@persister then
76
- # puts "CREATE PERSISTER #{name}, format: #{format}, kind: #{kind}"
77
- @@persister = @@persister_kinds[kind].new(name, format)
78
- if(nil != name) then
79
- @@known_persisters[name] = @@persister
80
- end
81
- end
82
-
83
- @@persister
84
- end
85
-
86
- def Xampl.print_known_persisters
87
- puts "Known Persisters:: --------------------------"
88
- @@known_persisters.each { | n, k |
89
- puts " #{n} #{k}"
90
- }
91
- puts "---------------------------------------------"
92
- end
93
-
94
- def Xampl.flush_persister_caches
95
- Xampl.print_known_persisters
96
- @@known_persisters.delete(@@persister.name)
97
- Xampl.print_known_persisters
98
- end
99
-
100
- def Xampl.drop_all_persisters
101
- puts "Drop All Persisters:: --------------------------"
102
- @@known_persisters.each { | n, k |
103
- puts " #{n} #{k}"
104
- }
105
- puts "---------------------------------------------"
106
- @@known_persisters = {}
107
- GC.start
108
- GC.start
109
- GC.start
110
- end
111
-
112
- def Xampl.drop_persister(name)
113
- Xampl.print_known_persisters
114
- @@known_persisters.delete(name)
115
- Xampl.print_known_persisters
116
- end
117
-
118
- @@xampl_lock = Sync.new
119
-
120
- def Xampl.transaction(thing, kind=nil, automatic=true, format=nil, &block)
121
- if String === thing then
122
- name = thing
123
- elsif XamplObject === thing then
124
- name = thing.persister.name
125
- else
126
- raise XamplException.new("can't base a transaction on a #{thing.class.name} (#{thing})")
127
- end
128
-
129
- if block_given? then
130
- @@xampl_lock.synchronize(:EX) do
131
- initial_persister = @@persister
132
- Xampl.enable_persister(name, kind, format)
133
-
134
- rollback = true
135
- exception = nil
136
- original_automatic = @@persister.automatic
137
- begin
138
- #TODO -- impose some rules on nested transactions/enable_persisters??
139
- Xampl.auto_persistence(automatic)
140
- result = yield
141
- Xampl.block_future_changes(true)
142
- Xampl.sync
143
- rollback = false
144
- return result
145
- rescue => e
146
- exception = e
147
- ensure
148
- Xampl.block_future_changes(false)
149
- Xampl.auto_persistence(original_automatic)
150
- if rollback then
151
- if exception then
152
- puts "ROLLBACK(#{__LINE__}):: #{exception}" if rollback
153
- print exception.backtrace.join("\n") if rollback
154
- else
155
- puts "ROLLBACK(#{__LINE__}):: UNKNOWN CAUSE" if rollback
156
- end
157
- end
158
- Xampl.rollback if rollback
159
- @@persister = initial_persister
160
- end
161
- end
162
- end
163
- end
164
-
165
- def Xampl.read_only_transaction(thing, kind=nil, automatic=true, format=nil, &block)
166
- if String === thing then
167
- name = thing
168
- elsif XamplObject === thing then
169
- name = thing.persister.name
170
- else
171
- raise XamplException.new("can't base a transaction on a #{thing.class.name} (#{thing})")
172
- end
173
-
174
- target_persister = nil
175
- if block_given? then
176
- @@xampl_lock.synchronize(:EX) do
177
- initial_persister = @@persister
178
- Xampl.enable_persister(name, kind, format)
179
- target_persister = @@persister
180
-
181
- rollback = true
182
- original_automatic = @@persister.automatic
183
- @changed ||= nil
184
- original_changed = @changed
185
- @changed = {}
186
- begin
187
- Xampl.auto_persistence(false)
188
- #Xampl.block_future_changes(true)
189
-
190
- yield
191
- rollback = false
192
- ensure
193
- Xampl.auto_persistence(original_automatic)
194
- #Xampl.block_future_changes(false)
195
-
196
- if 0 == @changed.size then
197
- @changed = original_changed
198
-
199
- puts "ROLLBACK(#{__LINE__})" if rollback
200
- Xampl.rollback if rollback
201
- @@persister = initial_persister
202
- else
203
- puts "CHANGED COUNT: #{@changed.size}"
204
- @changed = original_changed
205
-
206
- puts "ROLLBACK(#{__LINE__})" if rollback
207
- Xampl.rollback
208
-
209
- @@persister = initial_persister
210
-
211
- raise BlockedChange.new(target_persister)
212
- end
213
- end
214
- end
215
- end
216
- end
217
-
218
- def Xampl.read_only(target_persister)
219
- #TODO -- EXCLUSIVE ACCESS TO THE PERSISTER!!!
220
- name = target_persister.name
221
-
222
- if block_given? then
223
- initial_persister = @@persister
224
- Xampl.enable_persister(name, target_persister.kind, target_persister.format)
225
-
226
- rollback = true
227
- original_automatic = @@persister.automatic
228
- original_changed = @changed
229
- @changed = {}
230
- begin
231
- Xampl.auto_persistence(false)
232
- #Xampl.block_future_changes(true)
233
-
234
- yield
235
- rollback = false
236
- ensure
237
- #### Xampl.auto_persistence(original_automatic)
238
- #### #Xampl.block_future_changes(false)
239
- ####
240
- #### if 0 < @changed.size then
241
- #### puts "CHANGED COUNT: #{@changed.size}"
242
- #### raise BlockedChange.new(target_persister)
243
- #### end
244
- ####
245
- #### @changed = original_changed
246
- ####
247
- #### puts "ROLLBACK(#{__LINE__})" if rollback
248
- #### Xampl.rollback if rollback
249
- #### @@persister = initial_persister
250
-
251
- Xampl.auto_persistence(original_automatic)
252
- #Xampl.block_future_changes(false)
253
-
254
- if 0 == @changed.size then
255
- @changed = original_changed
256
-
257
- puts "ROLLBACK(#{__LINE__})" if rollback
258
- Xampl.rollback if rollback
259
- @@persister = initial_persister
260
- else
261
- puts "CHANGED COUNT: #{@changed.size}"
262
- @changed = original_changed
263
-
264
- puts "ROLLBACK(#{__LINE__})" if rollback
265
- Xampl.rollback
266
-
267
- @@persister = initial_persister
268
-
269
- raise BlockedChange.new(target_persister)
270
- end
271
- end
272
- end
273
- end
274
-
275
- def Xampl.introduce_to_persister(xampl)
276
- @@persister.introduce(xampl) if @@persister
277
- end
278
-
279
- def Xampl.count_changed
280
- @@persister.count_changed if @@persister
281
- end
282
-
283
- def Xampl.print_stats
284
- @@persister.print_stats if @@persister
285
- end
286
-
287
- def Xampl.auto_cache(xampl)
288
- if (nil == xampl.persister) and @@persister and @@persister.automatic then
289
- xampl.persister = @@persister
290
- end
291
- if xampl.persister and xampl.persister.automatic then
292
- xampl.persister.cache(xampl)
293
- end
294
- end
295
-
296
- def Xampl.auto_uncache(xampl)
297
- if xampl.persister and xampl.persister.automatic then
298
- xampl.persister.uncache(xampl)
299
- end
300
- end
301
-
302
- def Xampl.clear_cache
303
- @@persister.clear_cache if nil != @@persister
304
- end
305
-
306
- def Xampl.sync
307
- #raise XamplException.new(:live_across_rollback) if @@persister.rolled_back
308
- @@persister.sync if nil != @@persister
309
- end
310
-
311
- def Xampl.sync_all
312
- @@known_persisters.each{ | name, persister |
313
- persister.sync
314
- }
315
- end
316
-
317
- def Xampl.rollback(persister=@@persister)
318
- raise NoActivePersister unless persister
319
- persister.rollback_cleanup
320
- end
321
-
322
- def Xampl.rollback_all
323
- @@known_persisters.values.each{ | persister |
324
- persister.rollback
325
- }
326
- end
327
-
328
- def Xampl.lazy_load(xampl)
329
- pid = xampl.get_the_index
330
- if xampl and pid and @@persister then
331
- @@persister.lazy_load(xampl, xampl.class, pid) if xampl and pid and @@persister
332
- xampl.load_needed = false
333
- else
334
- puts "XAMPL.LAZY_LOAD -- REFUSED"
335
- end
336
- end
337
-
338
- def Xampl.lookup(klass, pid)
339
- @@persister.lookup(klass, pid) if nil != persister
340
- end
341
-
342
- def Xampl.find_known(klass, pid)
343
- xampl, ignore = @@persister.find_known(klass, pid) if nil != persister
344
- return xampl
345
- end
346
-
347
- def Xampl.write_to_cache(xampl)
348
- @@persister.write_to_cache(xampl)
349
- end
350
-
351
- def Xampl.cache(xampl)
352
- @@persister.cache(xampl)
353
- end
354
-
355
- def Xampl.lookup_in_map(map, klass, pid)
356
- return nil if nil == pid
357
-
358
- module_name = klass.module_name
359
- tag = klass.tag
360
-
361
- tag_map = map[module_name]
362
- return nil if nil == tag_map
363
-
364
- pid_map = tag_map[tag]
365
- return nil if nil == pid_map
366
-
367
- return pid_map[pid]
368
- end
369
-
370
- def Xampl.store_in_map(map, xampl)
371
- module_name = xampl.module_name
372
- tag = xampl.tag
373
- pid = xampl.get_the_index
374
-
375
- if nil == pid then
376
- return false
377
- end
378
-
379
- if block_given? then
380
- data = yield
381
- else
382
- data = xampl
383
- end
384
-
385
- tag_map = map[module_name]
386
- if nil == tag_map then
387
- tag_map = {}
388
- map[module_name] = tag_map
389
- end
390
-
391
- pid_map = tag_map[tag]
392
- if nil == pid_map then
393
- pid_map = {}
394
- tag_map[tag] = pid_map
395
- end
396
-
397
- pid_map[pid] = data
398
-
399
- return true
400
- end
401
-
402
- def Xampl.store_in_cache(map, xampl, container)
403
- module_name = xampl.module_name
404
- tag = xampl.tag
405
- pid = xampl.get_the_index
406
-
407
- if nil == pid then
408
- return false
409
- end
410
-
411
- if block_given? then
412
- data = yield
413
- else
414
- data = xampl
415
- end
416
-
417
- tag_map = map[module_name]
418
- if nil == tag_map then
419
- tag_map = {}
420
- map[module_name] = tag_map
421
- end
422
-
423
- pid_map = tag_map[tag]
424
- if nil == pid_map then
425
- pid_map = container.fresh_cache
426
- tag_map[tag] = pid_map
427
- end
428
-
429
- pid_map[pid] = data
430
-
431
- return true
432
- end
433
-
434
- def Xampl.remove_from_map(map, xampl)
435
- pid = xampl.get_the_index
436
- return nil unless pid
437
-
438
- tag_map = map[xampl.module_name]
439
- return nil unless tag_map
440
-
441
- pid_map = tag_map[xampl.tag]
442
- return nil unless pid_map
443
-
444
- return pid_map.delete(pid)
445
- end
446
-
447
- class Persister
448
- attr_accessor :name,
449
- :automatic,
450
- :block_changes,
451
- :read_count, :total_read_count,
452
- :write_count, :total_write_count,
453
- :total_sync_count, :total_rollback_count,
454
- :cache_hits, :total_cache_hits,
455
- :last_write_count,
456
- :rolled_back
457
- attr_reader :syncing, :format
458
-
459
- def initialize(name=nil, format=nil)
460
- @name = name
461
- @format = format
462
- @automatic = false
463
- @changed = {}
464
- @cache_hits = 0
465
- @total_cache_hits = 0
466
- @read_count = 0
467
- @total_read_count = 0
468
- @write_count = 0
469
- @total_write_count = 0
470
- @last_write_count = 0
471
- @total_sync_count = 0
472
- @total_rollback_count = 0
473
- @rolled_back = false
474
- @syncing = false
475
-
476
- @busy_count = 0
477
- end
478
-
479
- def busy(yes)
480
- if yes then
481
- @busy_count += 1
482
- elsif 0 < @busy_count then
483
- @busy_count -= 1
484
- end
485
- end
486
-
487
- def is_busy
488
- return 0 < @busy_count
489
- end
490
-
491
- def introduce(xampl)
492
- if xampl.introduce_persister(self) then
493
- cache(xampl)
494
- end
495
- has_changed(xampl) if xampl.is_changed
496
- end
497
-
498
- def has_changed(xampl)
499
- #raise XamplException.new(:live_across_rollback) if @rolled_back
500
- # puts "!!!! has_changed #{xampl} #{xampl.get_the_index} -- persist required: #{xampl.persist_required}"
501
- if xampl.persist_required && xampl.is_changed then
502
- unless self == xampl.persister
503
- raise MixedPersisters.new(xampl.persister, self)
504
- end
505
- @changed[xampl] = xampl
506
- # puts "!!!! change recorded ==> #{@changed.size}/#{count_changed} #{@changed.object_id} !!!!"
507
- # @changed.each{ | thing, ignore |
508
- # puts " changed: #{thing}, index: #{thing.get_the_index}, changed: #{thing.is_changed}"
509
- # }
510
- end
511
- end
512
-
513
- def has_not_changed(xampl)
514
- # puts "!!!! has_not_changed #{xampl} #{xampl.get_the_index} -- in @changed: #{nil != @changed[xampl]}"
515
- @changed.delete(xampl) if xampl
516
- end
517
-
518
- def count_changed
519
- # @changed.each{ | thing, ignore |
520
- # puts "changed: #{thing}, index: #{thing.get_the_index}"
521
- # }
522
- return @changed.size
523
- end
524
-
525
- def cache(xampl)
526
- raise XamplException.new(:unimplemented)
527
- end
528
-
529
- def uncache(xampl)
530
- raise XamplException.new(:unimplemented)
531
- end
532
-
533
- def clear_cache
534
- raise XamplException.new(:unimplemented)
535
- end
536
-
537
- def Persister.replace(old_xampl, new_xampl)
538
- pid = old_xampl.get_the_index
539
- if old_xampl.persister != @@persister then
540
- raise MixedPersisters.new(@@persister, old_xampl.persister)
541
- end
542
- if new_xampl.persister != @@persister then
543
- raise MixedPersisters.new(@@persister, new_xampl.persister)
544
- end
545
-
546
- new_xampl.note_replacing(old_xampl)
547
-
548
- unless old_xampl.load_needed then
549
- Xampl.log.warn("Replacing live xampl: #{old_xampl} pid: #{pid}")
550
- @@persister.uncache(old_xampl)
551
- old_xampl.invalidate
552
- end
553
- new_xampl.pid = nil
554
- new_xampl.pid = pid
555
- @@persister.introduce(new_xampl)
556
- end
557
-
558
- def represent(xampl)
559
- case @format
560
- when nil, :xml_format then
561
- return xampl.persist
562
- when :ruby_format then
563
- return xampl.to_ruby
564
- end
565
- end
566
-
567
- def realise(representation, target=nil)
568
- # Normally we'd expect to see the representation in the @format format, but
569
- # that isn't necessarily the case. Try to work out what the format might be...
570
-
571
- if representation =~ /^</ then
572
- return XamplObject.realise_from_xml_string(representation, target)
573
- else
574
- XamplObject.from_ruby(representation, target)
575
- end
576
- end
577
-
578
- def write(xampl)
579
- raise XamplException.new(:unimplemented)
580
- end
581
-
582
- def read(klass, pid, target=nil)
583
- raise XamplException.new(:unimplemented)
584
- end
585
-
586
- def lookup(klass, pid)
587
- #raise XamplException.new(:live_across_rollback) if @rolled_back
588
-
589
- # puts "LOOKUP:: klass: #{klass} pid: #{pid}"
590
-
591
- begin
592
- busy(true)
593
- xampl = read(klass, pid)
594
- ensure
595
- busy(false)
596
- end
597
-
598
- return xampl
599
- end
600
-
601
- def find_known(klass, pid)
602
- #raise XamplException.new(:live_across_rollback) if @rolled_back
603
-
604
- xampl = read_from_cache(klass, pid, nil)
605
-
606
- return xampl
607
- end
608
-
609
- def lazy_load(target, klass, pid)
610
- # puts "LAZY_LOAD:: klass: #{klass} pid: #{pid} target: #{target}"
611
-
612
- xampl = read(klass, pid, target)
613
-
614
- # puts " LAZY_LOAD --> #{xampl}"
615
-
616
- return xampl
617
- end
618
-
619
- def put_changed(msg="")
620
- puts "Changed::#{msg}:"
621
- @changed.each { | xampl, ignore | puts " #{xampl.tag} #{xampl.get_the_index}" }
622
- end
623
-
624
- def do_sync_write
625
- unchanged_in_changed_list = 0
626
- # puts "DO SYNC WRITE:: changed: #{@changed.size}, #{@changed.object_id}"
627
- @changed.each { | xampl, ignore |
628
- # puts " WRITE: #{xampl}, index: #{xampl.get_the_index}, changed: #{xampl.is_changed}"
629
-
630
- unchanged_in_changed_list += 1 unless xampl.is_changed
631
- write(xampl) unless xampl.kind_of?(InvalidXampl)
632
- }
633
- end
634
-
635
- def sync
636
- #raise XamplException.new(:live_across_rollback) if @rolled_back
637
- begin
638
- #puts "SYNC"
639
- #puts "SYNC"
640
- #puts "SYNC changed: #{@changed.size}"
641
- #@changed.each do | key, value |
642
- # #puts "key: #{key.class.name}, value: #{value.class.name}"
643
- # puts key.to_xml
644
- #end
645
- #puts "SYNC"
646
- #puts "SYNC"
647
- busy(true)
648
- @syncing = true
649
-
650
- do_sync_write
651
-
652
- @changed = {}
653
-
654
- @total_read_count = @total_read_count + @read_count
655
- @total_write_count = @total_write_count + @write_count
656
- @total_cache_hits = @total_cache_hits + @cache_hits
657
- @total_sync_count = @total_sync_count + 1
658
-
659
- @read_count = 0
660
- @last_write_count = @write_count
661
- @write_count = 0
662
-
663
- self.sync_done()
664
-
665
- return @last_write_count
666
- ensure
667
- busy(false)
668
- @syncing = false
669
- end
670
- end
671
-
672
- def sync_done
673
- raise XamplException.new(:unimplemented)
674
- end
675
-
676
- def rollback
677
- begin
678
- busy(true)
679
-
680
- return Xampl.rollback(self)
681
- ensure
682
- busy(false)
683
- end
684
- end
685
-
686
- def rollback_cleanup
687
- @changed = {}
688
- end
689
-
690
- def print_stats
691
- printf("SYNC:: TOTAL cache_hits: %d, reads: %d, writes: %d\n",
692
- @total_cache_hits, @total_read_count, @total_write_count)
693
- printf(" cache_hits: %d, reads: %d, last writes: %d\n",
694
- @cache_hits, @read_count, @last_write_count)
695
- printf(" syncs: %d\n", @total_sync_count)
696
- printf(" changed count: %d (%d)\n", count_changed, @changed.size)
697
- @changed.each{ | thing, ignore |
698
- if thing.is_changed then
699
- puts " changed: #{thing}, index: #{thing.get_the_index}"
700
- else
701
- puts " UNCHANGED: #{thing}, index: #{thing.get_the_index} <<<<<<<<<<<<<<<<<<< BAD!"
702
- end
703
- }
704
- end
705
- end
706
-
707
- class NoActivePersister < Exception
708
- def message
709
- "No Persister is active"
710
- end
711
- end
712
-
713
- class BlockedChange < Exception
714
- attr_reader :xampl
715
-
716
- def initialize(xampl=nil)
717
- @xampl = xampl
718
- end
719
-
720
- def message
721
- "attempt to change #{@xampl}, pid: #{@xampl.get_the_index}, oid: #{@xampl.object_id} when changes are blocked"
722
- end
723
- end
724
-
725
- class UnmanagedChange < Exception
726
- attr_reader :xampl
727
-
728
- def initialize(xampl=nil)
729
- @xampl = xampl
730
- end
731
-
732
- def message
733
- "attempt to change #{@xampl}, pid: #{@xampl.get_the_index}, oid: #{@xampl.object_id} outside of its persister's management"
734
- end
735
- end
736
-
737
- class IncompatiblePersisterRequest < Exception
738
- attr_reader :msg
739
- def initialize(persister, feature_name, requested_feature_value, actual_feature_value)
740
- @msg = "persister #{persister.name}:: requested feature: #{feature_name} #{requested_feature_value}, actual: #{actual_feature_value}"
741
- end
742
-
743
- def message
744
- @msg
745
- end
746
- end
747
-
748
- class MixedPersisters < Exception
749
- attr_reader :msg
750
- def initialize(active, local)
751
- @msg = "mixed persisters:: active #{active.name}, local: #{local.name}"
752
- end
753
-
754
- def message
755
- @msg
756
- end
757
- end
758
-
759
- require "persister/simple"
760
- require "persister/in-memory"
761
- require "persister/filesystem"
762
- end
763
-