hprevalence 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,18 @@
1
+
2
+ Thanks for using HPrevalence!
3
+
4
+ Don't forget:
5
+
6
+ - You'll need RubyGems to install it:
7
+ Grab it http://rubygems.rubyforge.org
8
+
9
+ And HPrevalence depends on
10
+
11
+ - Guid
12
+ http://guid.rubyforge.org
13
+
14
+ - Rake (if you want to execute the test cases)
15
+ http://rake.rubyforge.org
16
+
17
+
18
+ Enjoy!
@@ -0,0 +1,243 @@
1
+
2
+ require 'thread'
3
+ require 'guid'
4
+ require 'transparent'
5
+ require 'internal/command_logger'
6
+ require 'internal/iomanager'
7
+ require 'internal/serializer'
8
+ require 'internal/store_manager'
9
+
10
+ module HPrevalence
11
+
12
+ #
13
+ class EngineBuilder
14
+
15
+ def self.build( target_dir, &system_init )
16
+ system = system_init.call
17
+ SimpleEngine.new( target_dir, system )
18
+ end
19
+
20
+ def self.build_transparent( target_dir, &system_init )
21
+ system = system_init.call
22
+ TransparentEngine.new( target_dir, system )
23
+ end
24
+
25
+ end
26
+
27
+ #
28
+ class SimpleEngine
29
+
30
+ attr_reader :system, :target_dir
31
+
32
+ def initialize( target_dir, system, store_manager = StorageManager.new(target_dir) )
33
+ @lock = Mutex.new
34
+ @target_dir, @system, @store_manager = target_dir, system, store_manager
35
+ @system = @store_manager.restore( system() )
36
+ end
37
+
38
+ def execute_command( command )
39
+ raise 'Invalid command' unless command.respond_to?( :execute )
40
+ @lock.synchronize {
41
+ @store_manager.store( command )
42
+ return command.execute( @system )
43
+ }
44
+ end
45
+
46
+ def close
47
+ @store_manager.close
48
+ end
49
+
50
+ def take_snapshot
51
+ @lock.synchronize {
52
+ @store_manager.take_snapshot @system
53
+ }
54
+ end
55
+
56
+ end
57
+
58
+ #
59
+ # Holds only one system
60
+ #
61
+ class TransparentEngine < SimpleEngine
62
+ @@engines = Hash.new()
63
+ @@class_lock = Mutex.new
64
+
65
+ attr_reader :engine_id
66
+
67
+ def initialize( target_dir, system )
68
+ @proxies_lock = Mutex.new
69
+ @engine_id = Guid.new()
70
+
71
+ TransparentEngine.register_engine( self )
72
+
73
+ @proxies = []
74
+ add_engine_id_method( system )
75
+ store_manager = TransparentStorageManager.new(self, target_dir)
76
+ within_engine {
77
+ @proxied_system = HPrevalence::Transparent::TransparentProxy.new( system )
78
+ }
79
+
80
+ super( target_dir, system, store_manager )
81
+ end
82
+
83
+ def system
84
+ @proxied_system
85
+ end
86
+
87
+ def close
88
+ super
89
+ TransparentEngine.unregister_engine( self )
90
+ end
91
+
92
+ def take_snapshot
93
+ @lock.synchronize {
94
+ begin
95
+ Thread.current[:references] = Hash.new
96
+ within_engine {
97
+ @store_manager.take_snapshot @proxied_system
98
+ }
99
+ ensure
100
+ Thread.current[:references] = nil
101
+ end
102
+ }
103
+ end
104
+
105
+ def execute_command(command)
106
+ super(command)
107
+ end
108
+
109
+ def register_proxy(proxy)
110
+ # TODO: Synchronization here?
111
+ @proxies << proxy
112
+ @proxies.length - 1
113
+ end
114
+
115
+ def proxy_by_id( id, related_symbol = nil, parent_proxy_id = nil )
116
+ p = @proxies[ id ]
117
+ return p unless p.nil?
118
+ if !related_symbol.nil? && !parent_proxy_id.nil?
119
+ return create_nested_proxy( parent_proxy_id, related_symbol )
120
+ end
121
+ nil
122
+ end
123
+
124
+ def create_nested_proxy( parent_proxy_id, related_symbol )
125
+ @proxies_lock.synchronize {
126
+ parent_proxy = proxy_by_id( parent_proxy_id )
127
+ target = parent_proxy.target
128
+ nested_target = target.send( related_symbol )
129
+ new_proxy = HPrevalence::Transparent::NestedTransparentProxy.new( nested_target, related_symbol, parent_proxy_id )
130
+ target._set_proxy( related_symbol, new_proxy )
131
+ return new_proxy
132
+ }
133
+ end
134
+
135
+ def within_engine()
136
+ begin
137
+ Thread.current[:engine] = self
138
+ yield
139
+ ensure
140
+ Thread.current[:engine] = nil
141
+ end
142
+ end
143
+
144
+ def restore(proxy)
145
+ proxy.engine_id = @engine_id
146
+ p = proxy_by_id(proxy.proxy_id)
147
+ if (p.nil?)
148
+ proxy.proxy_id = register_proxy(proxy)
149
+ p = proxy
150
+ end
151
+ p
152
+ end
153
+
154
+ def self.create_nested_proxy( parent_proxy_id, symbol, engine_id )
155
+ within_engine( engine_id ) { |engine|
156
+ engine.create_nested_proxy( parent_proxy_id, symbol )
157
+ }
158
+ end
159
+
160
+ def self.within_engine( engine_id )
161
+ engine = @@engines[ engine_id ]
162
+ raise 'Engine not found' if engine.nil?
163
+ begin
164
+ # puts 'engine>'
165
+ Thread.current[:engine] = engine
166
+ yield(engine)
167
+ ensure
168
+ Thread.current[:engine] = nil
169
+ # puts 'engine<'
170
+ end
171
+ end
172
+
173
+ def self.current()
174
+ engine = Thread.current[:engine]
175
+
176
+ if block_given?
177
+ # puts 'engine.current'
178
+ raise 'Engine not found' if engine.nil?
179
+ yield( engine )
180
+ # puts 'end.current'
181
+ else
182
+ return engine
183
+ end
184
+ end
185
+
186
+ protected
187
+
188
+ def self.register_engine( engine )
189
+ @@class_lock.synchronize {
190
+ @@engines[engine.engine_id] = engine
191
+ }
192
+ end
193
+
194
+ def self.unregister_engine( engine )
195
+ @@class_lock.synchronize {
196
+ @@engines.delete(engine.engine_id)
197
+ }
198
+ end
199
+
200
+ def add_engine_id_method( system )
201
+ temp_id = @engine_id
202
+ system.instance_eval {
203
+ @engine_id = temp_id
204
+ }
205
+ # (class << system; self;end).class_eval do
206
+ # define_method(:_engine_id) { @engine_id }
207
+ # end
208
+ end
209
+
210
+ end
211
+
212
+ class PrevalenceService
213
+ attr_reader :engine
214
+
215
+ def self.create_service( snapshot_interval = 60 * 60 * 1000, &engine_init )
216
+ engine = engine_init.call
217
+ raise 'Engine not created' if engine.nil?
218
+ PrevalenceService.new( engine, snapshot_interval )
219
+ end
220
+
221
+ def system
222
+ @engine.system
223
+ end
224
+
225
+ protected
226
+
227
+ def initialize( engine, snapshot_interval )
228
+ @engine = engine
229
+ @snapshot_interval = snapshot_interval
230
+ end
231
+
232
+ def start_snapshot_thread
233
+ Thread.new(self) {
234
+ while true
235
+ sleep(@snapshot_interval)
236
+ engine.take_snapshot
237
+ end
238
+ }
239
+ end
240
+
241
+ end
242
+
243
+ end
@@ -0,0 +1,98 @@
1
+
2
+ class BinaryCommandSerializer
3
+
4
+ def initialize( io )
5
+ raise 'Invalid stream' if io.nil?
6
+ @io = io
7
+ @io.binmode
8
+ # register_finalize()
9
+ end
10
+
11
+ def <<(command)
12
+ raise 'Invalid command' if command.nil?
13
+ Marshal.dump( command, @io )
14
+ @io.flush
15
+ @io.fsync
16
+ end
17
+
18
+ def self.restore_from_io( io, system )
19
+ # Theorically this should work and its more elegant than the current version
20
+ # Marshal.load( io ) { |command|
21
+ # command.execute( system )
22
+ # }
23
+ while( !io.eof? )
24
+ command = Marshal.load( io )
25
+ command.execute( system )
26
+ end
27
+ end
28
+
29
+ def close
30
+ @io.close
31
+ # unregister_finalize
32
+ end
33
+ #
34
+ # protected
35
+ #
36
+ # def register_finalize
37
+ # ObjectSpace.define_finalizer( self ) {
38
+ # |logger|
39
+ # logger.close
40
+ # }
41
+ # end
42
+ #
43
+ # def unregister_finalize
44
+ # ObjectSpace.undefine_finalizer( self )
45
+ # end
46
+
47
+ end
48
+
49
+ class CommandLogger
50
+
51
+ Logger_Extension = 'commandlog'
52
+
53
+ attr_reader :current_file_name
54
+
55
+ def initialize( iomanager )
56
+ @iomanager = iomanager
57
+ end
58
+
59
+ def create_command_store()
60
+ stream = @iomanager.create_new_stream( Logger_Extension )
61
+ @current_file_name = stream.full_name
62
+ BinaryCommandSerializer.new( stream )
63
+ end
64
+
65
+ def log_files
66
+ @iomanager.list( Logger_Extension )
67
+ end
68
+
69
+ # removes all command_logs as a snapshot has been taken
70
+ def reset_commands()
71
+ streams = @iomanager.obtain_ordered_read_streams( Logger_Extension )
72
+
73
+ streams.each do |stream|
74
+ stream.close
75
+ File.delete stream.full_name
76
+ end
77
+
78
+ end
79
+
80
+ def restore( system )
81
+ streams = @iomanager.obtain_ordered_read_streams( Logger_Extension )
82
+
83
+ begin
84
+
85
+ streams.each do |stream|
86
+ BinaryCommandSerializer.restore_from_io( stream, system )
87
+ end
88
+
89
+ ensure
90
+ streams.each do |stream|
91
+ stream.close
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
@@ -0,0 +1,83 @@
1
+
2
+
3
+ class FileIOManager
4
+
5
+ attr_reader :positions, :target_dir
6
+
7
+ def initialize( target_dir, positions = 12 )
8
+ @target_dir, @positions = target_dir, positions
9
+ end
10
+
11
+ def create_new_stream( extension )
12
+ file_name = obtain_next_available( extension )
13
+ create_write_stream( file_name )
14
+ end
15
+
16
+ def list( extension )
17
+ unless File.exist?( @target_dir )
18
+ return []
19
+ end
20
+ dir = File.join( @target_dir, "*.#{extension}" )
21
+ valid_entries = Dir.entries( @target_dir ).select do |entry|
22
+ entry =~ /^\d{#{positions}}\.#{extension}$/
23
+ end
24
+ valid_entries.sort
25
+ end
26
+
27
+ def obtain_ordered_read_streams( extension )
28
+ streams = []
29
+
30
+ list( extension ).each do |file|
31
+ file_name = File.join( @target_dir, file )
32
+ streams << create_read_stream( file_name )
33
+ end
34
+
35
+ streams
36
+ end
37
+
38
+ def obtain_latest_read_stream( extension )
39
+ files = list( extension )
40
+ return nil if files.length == 0
41
+
42
+ file_name = File.join( @target_dir, files.max )
43
+ create_read_stream( file_name )
44
+ end
45
+
46
+ protected
47
+
48
+ def create_read_stream( file_name )
49
+ file = File.new( file_name, "rb" )
50
+ add_helper_methods( file, file_name )
51
+ end
52
+
53
+ def create_write_stream( file_name )
54
+ file = File.new( file_name, "wb" )
55
+ add_helper_methods( file, file_name )
56
+ end
57
+
58
+ def add_helper_methods( stream, file_name )
59
+ (class << stream; self; end).class_eval do
60
+ define_method(:full_name) { file_name }
61
+ define_method(:basename) { File.basename(file_name) }
62
+ end
63
+ stream
64
+ end
65
+
66
+ def obtain_hightest_num( extension )
67
+ val = 0
68
+ list( extension ).each do |file|
69
+ new_val = file.slice( 0, positions ).to_i
70
+ if new_val > val
71
+ val = new_val
72
+ end
73
+ end
74
+ val
75
+ end
76
+
77
+ def obtain_next_available( extension )
78
+ val = obtain_hightest_num( extension )
79
+ sprintf("%s/%0#{positions}d.%s", @target_dir, val + 1, extension)
80
+ end
81
+
82
+ end
83
+
@@ -0,0 +1,68 @@
1
+
2
+ class ObjectGraph
3
+
4
+ def initialize()
5
+ @root = nil
6
+ @registry = Hash.new()
7
+ end
8
+
9
+ def self.build( node )
10
+ graph = ObjectGraph.new()
11
+ graph.root = node
12
+ graph
13
+ end
14
+
15
+ def root=( node )
16
+ @root = GraphItem.new( self, node )
17
+ end
18
+
19
+ def root
20
+ @root.object
21
+ end
22
+
23
+ def object_reference( var )
24
+ graph_item = @registry[ var ]
25
+ if (graph_item.nil?)
26
+ graph_item = GraphItem.new( self, var )
27
+ @registry[ var ] = graph_item
28
+ end
29
+ graph_item
30
+ end
31
+
32
+ end
33
+
34
+ class GraphItem
35
+
36
+ def initialize( graph, node )
37
+ @instance_vars = Hash.new
38
+ @node_type = node.class
39
+
40
+ node.instance_variables.each do |var|
41
+ name = var.slice(1, var.length - 1)
42
+ @instance_vars[ name ] = graph.object_reference( node.instance_eval(var) )
43
+ end
44
+ end
45
+
46
+ def object
47
+ obj = @node_type.allocate
48
+ restore_instance_vars obj
49
+ obj
50
+ end
51
+
52
+ protected
53
+
54
+ def restore_instance_vars(obj)
55
+ @instance_vars.each do |name, value|
56
+
57
+ var = value.object
58
+
59
+ setter = name + "="
60
+ if obj.respond_to?(setter)
61
+ obj.__send__(setter, var)
62
+ else
63
+ obj.instance_eval("@#{ name } = var")
64
+ end
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,83 @@
1
+
2
+ class DefaultModelSerializer
3
+
4
+ Snapshot_Extension = 'snapshot'
5
+
6
+ def initialize( iomanager )
7
+ @iomanager = iomanager
8
+ end
9
+
10
+ def dump( object_model )
11
+ begin
12
+ stream = @iomanager.create_new_stream( Snapshot_Extension )
13
+ Marshal.dump( object_model, stream )
14
+ ensure
15
+ stream.close
16
+ end
17
+ end
18
+
19
+ def load()
20
+ object_model = nil
21
+ stream = @iomanager.obtain_latest_read_stream( Snapshot_Extension )
22
+ return nil if stream.nil?
23
+
24
+ begin
25
+ object_model = Marshal.load( stream )
26
+ ensure
27
+ stream.close
28
+ end
29
+
30
+ object_model
31
+ end
32
+
33
+ end
34
+
35
+ #class SmartModelSerializer
36
+ #
37
+ # Snapshot_Extension = 'ssnapshot'
38
+ #
39
+ # def initialize( iomanager )
40
+ # @iomanager = iomanager
41
+ # end
42
+ #
43
+ # def dump( object_model )
44
+ #
45
+ # begin
46
+ # stream = @iomanager.create_new_stream( Snapshot_Extension )
47
+ # graph = ObjectGraph.build( object_model )
48
+ # write_object_tree( stream, graph )
49
+ # ensure
50
+ # stream.close
51
+ # end
52
+ #
53
+ # end
54
+ #
55
+ # def load()
56
+ #
57
+ # object_model = nil
58
+ #
59
+ # begin
60
+ # stream = @iomanager.obtain_latest_read_stream( Snapshot_Extension )
61
+ # graph = read_object_tree( stream )
62
+ # object_model = graph.root
63
+ # ensure
64
+ # stream.close
65
+ # end
66
+ #
67
+ # object_model
68
+ #
69
+ # end
70
+ #
71
+ # protected
72
+ #
73
+ # def write_object_tree( stream, object )
74
+ # Marshal.dump( object, stream )
75
+ # end
76
+ #
77
+ # def read_object_tree( stream )
78
+ # Marshal.load( stream )
79
+ # end
80
+ #
81
+ #end
82
+
83
+
@@ -0,0 +1,74 @@
1
+
2
+ module HPrevalence
3
+
4
+ class StorageManager
5
+
6
+ def initialize( target_dir, iomanager = FileIOManager.new(target_dir), logger = CommandLogger.new(iomanager), model_serializer = DefaultModelSerializer.new(iomanager) )
7
+ @target_dir, @model_serializer = target_dir, model_serializer
8
+ @logger = logger
9
+ @comand_store = nil
10
+ end
11
+
12
+ def restore( system )
13
+ system = restore_snapshot( system )
14
+ restore_commands( system )
15
+ system
16
+ end
17
+
18
+ def store( command )
19
+ ensure_command_store
20
+ @comand_store << command
21
+ end
22
+
23
+ def take_snapshot( system )
24
+ @model_serializer.dump( system )
25
+ close()
26
+ @logger.reset_commands
27
+ end
28
+
29
+ def close
30
+ @comand_store.close unless @comand_store.nil?
31
+ @comand_store = nil
32
+ end
33
+
34
+ protected
35
+
36
+ def ensure_command_store
37
+ @comand_store ||= @logger.create_command_store()
38
+ end
39
+
40
+ def restore_commands( system )
41
+ @logger.restore( system )
42
+ end
43
+
44
+ def restore_snapshot( system )
45
+ system_new = @model_serializer.load
46
+ if !system_new.nil?
47
+ system = system_new
48
+ end
49
+ system
50
+ end
51
+
52
+ end
53
+
54
+ #
55
+ #
56
+ #
57
+ class TransparentStorageManager < StorageManager
58
+
59
+ def initialize( engine, target_dir, iomanager = FileIOManager.new(target_dir), logger = CommandLogger.new(iomanager), model_serializer = DefaultModelSerializer.new(iomanager) )
60
+ super( target_dir, iomanager, logger, model_serializer )
61
+ @engine = engine
62
+ end
63
+
64
+ def restore( system )
65
+ @engine.within_engine {
66
+ system = restore_snapshot( system )
67
+ restore_commands( system )
68
+ }
69
+ system
70
+ end
71
+
72
+ end
73
+
74
+ end