xamplr 1.9.0 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-