rservicebus 0.1.57 → 0.1.58

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,19 @@
1
1
  module RServiceBus
2
2
 
3
- require 'zlib'
4
-
3
+ require "zlib"
4
+ require "yaml"
5
+ require "uuidtools"
6
+
5
7
  #This is the top level message that is passed around the bus
6
8
  class Message
7
9
 
8
- attr_reader :returnAddress, :msgId, :remoteQueueName, :remoteHostName, :lastErrorSourceQueue, :lastErrorString
10
+ attr_reader :returnAddress, :msgId, :remoteQueueName, :remoteHostName, :lastErrorSourceQueue, :lastErrorString, :correlationId
9
11
 
10
12
  # Constructor
11
13
  #
12
14
  # @param [Object] msg The msg to be sent
13
15
  # @param [Object] returnAddress A queue to which the destination message handler can send replies
14
- def initialize( msg, returnAddress )
16
+ def initialize( msg, returnAddress, correlationId=nil )
15
17
  if ENV["RSBMSG_COMPRESS"].nil? then
16
18
  @compressed = false
17
19
  @_msg=YAML::dump(msg)
@@ -20,6 +22,7 @@ module RServiceBus
20
22
  @_msg=Zlib::Deflate.deflate(YAML::dump(msg))
21
23
  end
22
24
 
25
+ @correlationId = correlationId
23
26
  @returnAddress=returnAddress
24
27
 
25
28
  @createdAt = DateTime.now
@@ -27,8 +27,8 @@ module RServiceBus
27
27
 
28
28
  return false
29
29
  end
30
-
31
-
30
+
31
+
32
32
  def ProcessToHash( payload )
33
33
  headLine = payload.shift
34
34
  newPayload = Array.new
@@ -0,0 +1,72 @@
1
+ module RServiceBus
2
+
3
+ #Co0ordinate Transactions across resources, handlers, and Sagas
4
+ class ResourceManager
5
+
6
+ # Constructor
7
+ #
8
+ def initialize( stateManager, sagaStorage )
9
+ @appResources = Hash.new
10
+ @currentResources = Hash.new
11
+ @stateManager = stateManager
12
+ @sagaStorage = sagaStorage
13
+ end
14
+
15
+ def add( name, res )
16
+ @appResources[name] = res
17
+ end
18
+
19
+ def getAll
20
+ return @appResources
21
+ end
22
+
23
+ def Begin
24
+ @currentResources = Hash.new
25
+ @stateManager.Begin
26
+ @sagaStorage.Begin
27
+ end
28
+
29
+ def get( name )
30
+ if @currentResources[name].nil? then
31
+ r = @appResources[name]
32
+ r._connect
33
+ r.Begin
34
+ RServiceBus.rlog "Preparing resource: #{resourceName}. Begin"
35
+ end
36
+ @currentResources[name] = @appResources[name]
37
+ return @appResources[name]
38
+ end
39
+
40
+ def Commit( msgName )
41
+ @stateManager.Commit
42
+ @sagaStorage.Commit
43
+ RServiceBus.rlog "HandlerManager.commitResourcesUsedToProcessMsg, #{msgName}"
44
+ @currentResources.each do |k,v|
45
+ RServiceBus.rlog "Commit resource, #{v.class.name}"
46
+ v.Commit
47
+ v.finished
48
+ end
49
+
50
+ end
51
+
52
+ def Rollback( msgName )
53
+ @sagaStorage.Rollback
54
+ RServiceBus.rlog "HandlerManager.rollbackResourcesUsedToProcessMsg, #{msgName}"
55
+ @currentResources.each do |k,v|
56
+ begin
57
+ RServiceBus.rlog "Rollback resource, #{v.class.name}"
58
+ v.Rollback
59
+ v.finished
60
+ rescue Exception => e1
61
+ @host.log "Caught nested exception rolling back, #{v.class.name}, for msg, #{msgName}"
62
+ @host.log "****"
63
+ @host.log e1.message
64
+ @host.log e1.backtrace
65
+ @host.log "****"
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,22 @@
1
+ module RServiceBus
2
+
3
+
4
+ class Saga_Base
5
+ attr_accessor :data
6
+
7
+ def initialize
8
+ @finished = false
9
+ end
10
+
11
+ def sendTimeout( msg, milliseconds )
12
+ end
13
+
14
+ def finish
15
+ @data.finished = true
16
+ end
17
+
18
+ end
19
+
20
+
21
+ end
22
+
@@ -0,0 +1,25 @@
1
+ module RServiceBus
2
+
3
+
4
+ class Saga_Data
5
+ attr_reader :correlationId, :sagaClassName
6
+ attr_accessor :finished
7
+
8
+ def initialize( saga )
9
+ @createdAt = DateTime.now
10
+ @correlationId = UUIDTools::UUID.random_create
11
+ @sagaClassName = saga.class.name
12
+ @finished = false
13
+
14
+ @hash = {}
15
+ end
16
+
17
+ def method_missing(name, *args, &block)
18
+ @hash.send(name, *args, &block)
19
+ end
20
+
21
+ end
22
+
23
+
24
+ end
25
+
@@ -0,0 +1,135 @@
1
+ module RServiceBus
2
+
3
+
4
+ class Saga_Manager
5
+
6
+ def initialize( host, resourceManager, sagaStorage )
7
+ @handler = Hash.new
8
+ @startWith = Hash.new
9
+ @saga = Hash.new
10
+
11
+ @host = host
12
+
13
+ @resourceManager = resourceManager
14
+ @resourceListBySagaName = Hash.new
15
+
16
+ @sagaStorage = sagaStorage
17
+ end
18
+
19
+
20
+ def GetMethodsByPrefix( saga, prefix )
21
+ list = []
22
+ saga.instance_methods.each do |name|
23
+ list.push name.to_s.sub( prefix, "" ) if name.to_s.slice( 0,prefix.length ) == prefix
24
+ end
25
+
26
+ return list
27
+ end
28
+
29
+ def GetStartWithMethodNames( saga )
30
+ return self.GetMethodsByPrefix( saga, "StartWith_" )
31
+ end
32
+
33
+ # setBusAttributeIfRequested
34
+ #
35
+ # @param [RServiceBus::Saga] saga
36
+ def setBusAttributeIfRequested( saga )
37
+ if defined?( saga.Bus ) then
38
+ saga.Bus = @host
39
+ RServiceBus.log "Bus attribute set for: " + saga.class.name
40
+ end
41
+
42
+ return self
43
+ end
44
+
45
+ def interrogateSagaForAppResources( saga )
46
+ RServiceBus.rlog "Checking app resources for: #{saga.class.name}"
47
+ RServiceBus.rlog "If your attribute is not getting set, check that it is in the 'attr_accessor' list"
48
+
49
+ @resourceListBySagaName[saga.class.name] = Array.new
50
+ @resourceManager.getAll.each do |k,v|
51
+ if saga.class.method_defined?( k ) then
52
+ @resourceListBySagaName[saga.class.name] << k
53
+ RServiceBus.log "Resource attribute, #{k}, found for: " + saga.class.name
54
+ end
55
+ end
56
+
57
+ return self
58
+ end
59
+
60
+
61
+ def RegisterSaga( saga )
62
+ s = saga.new
63
+ self.setBusAttributeIfRequested( s )
64
+
65
+ self.GetStartWithMethodNames( saga ).each do |msgName|
66
+ @startWith[msgName] = Array.new if @startWith[msgName].nil?
67
+ @startWith[msgName] << s
68
+
69
+ RServiceBus.log "Registered, #{saga.name}, to StartWith, #{msgName}"
70
+ end
71
+
72
+ @saga[saga.name] = s
73
+ end
74
+
75
+
76
+ def prepSaga( saga )
77
+ if !@resourceListBySagaName[saga.class.name].nil? then
78
+ @resourceListBySagaName[saga.class.name].each do |k,v|
79
+ saga.instance_variable_set( "@#{k}", resourceManager.get(k).getResource() )
80
+ RServiceBus.rlog "App resource attribute, #{k}, set for: " + saga.class.name
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ def Handle( rmsg )
87
+ @resourcesUsed = Hash.new
88
+ handled = false
89
+ msg = rmsg.msg
90
+
91
+ RServiceBus.log "SagaManager, started processing, #{msg.class.name}", true
92
+ if !@startWith[msg.class.name].nil? then
93
+ @startWith[msg.class.name].each do |saga|
94
+ data = Saga_Data.new( saga )
95
+ @sagaStorage.Set( data )
96
+
97
+ methodName = "StartWith_#{msg.class.name}"
98
+ self.ProcessMsg( saga, data, methodName, msg )
99
+
100
+ handled = true
101
+ end
102
+ end
103
+ return handled if handled == true
104
+
105
+
106
+ data = @sagaStorage.Get( rmsg.correlationId )
107
+ return handled if data.nil?
108
+ methodName = "Handle_#{msg.class.name}"
109
+ saga = @saga[data.sagaClassName];
110
+ self.ProcessMsg( saga, data, methodName, msg );
111
+
112
+
113
+ return true
114
+ end
115
+
116
+ def ProcessMsg( saga, data, methodName, msg )
117
+ @host.sagaData = data
118
+ saga.data = data
119
+
120
+ if saga.class.method_defined?( methodName ) then
121
+ saga.send methodName, msg
122
+ end
123
+
124
+ if data.finished == true then
125
+ @sagaStorage.Delete data.correlationId
126
+ end
127
+
128
+ @host.sagaData = nil
129
+ #Save Data
130
+ end
131
+ end
132
+
133
+
134
+ end
135
+
@@ -0,0 +1,130 @@
1
+ module RServiceBus
2
+
3
+ #Given a directory, this class is responsible loading Sagas
4
+ class SagaLoader
5
+
6
+ attr_reader :sagaList
7
+
8
+ @listOfLoadedPaths
9
+
10
+ # Constructor
11
+ #
12
+ # @param [RServiceBus::Host] host instance
13
+ # @param [Hash] appResources As hash[k,v] where k is the name of a resource, and v is the resource
14
+ def initialize( host, sagaManager )
15
+ @host = host
16
+
17
+ @sagaManager = sagaManager
18
+
19
+ @listOfLoadedPaths = Hash.new
20
+ end
21
+
22
+ # Cleans the given path to ensure it can be used for as a parameter for the require statement.
23
+ #
24
+ # @param [String] filePath the path to be cleaned
25
+ def getRequirePath( filePath )
26
+ if !filePath.start_with?( "/" ) then
27
+ filePath = "./" + filePath
28
+ end
29
+
30
+ if File.exists?( filePath ) then
31
+ return filePath.sub( ".rb", "")
32
+ end
33
+
34
+ abort( "Filepath, " + filePath + ", given for Saga require doesn't exist" );
35
+ end
36
+
37
+ # Instantiate the saga named in sagaName from the file name in filePath
38
+ # Exceptions will be raised if encountered when loading sagas. This is a load time activity,
39
+ # so sagas should load correctly. As much information as possible is returned
40
+ # to enable the saga to be fixed, or configuration corrected.
41
+ #
42
+ # @param [String] sagaName name of the saga to instantiate
43
+ # @param [String] filePath the path to the file to be loaded
44
+ # @return [RServiceBus::Saga] the loader
45
+ def loadSagaFromFile( sagaName, filePath )
46
+ requirePath = self.getRequirePath( filePath )
47
+
48
+ require requirePath
49
+ begin
50
+ saga = Object.const_get(sagaName);
51
+ rescue Exception => e
52
+ puts "Expected class name: " + sagaName + ", not found after require: " + requirePath
53
+ puts "**** Check in " + filePath + " that the class is named : " + sagaName
54
+ puts "( In case its not that )"
55
+ raise e
56
+ end
57
+
58
+ return saga
59
+ end
60
+
61
+ # Wrapper function
62
+ #
63
+ # @param [String] filePath
64
+ # @param [String] sagaName
65
+ # @returns [RServiceBus::Saga] saga
66
+ def loadSaga(filePath, sagaName)
67
+ if @listOfLoadedPaths.has_key?( filePath ) then
68
+ RServiceBus.log "Not reloading, #{filePath}"
69
+ return
70
+ end
71
+
72
+ begin
73
+ RServiceBus.rlog "filePath: " + filePath
74
+ RServiceBus.rlog "sagaName: " + sagaName
75
+
76
+ saga = self.loadSagaFromFile( sagaName, filePath )
77
+ RServiceBus.log "Loaded Saga: " + sagaName
78
+
79
+ @sagaManager.RegisterSaga( saga )
80
+
81
+ @listOfLoadedPaths[filePath] = 1
82
+ rescue Exception => e
83
+ puts "Exception loading saga from file: " + filePath
84
+ puts e.message
85
+ puts e.backtrace[0]
86
+
87
+ abort()
88
+ end
89
+
90
+ end
91
+
92
+ #This method is overloaded for unit tests
93
+ #
94
+ # @param [String] path directory to check
95
+ # @return [Array] a list of paths to files found in the given path
96
+ def getListOfFilesForDir( path )
97
+ return Dir[path + "/*"];
98
+ end
99
+
100
+ #Extract the top level dir or file name as it is the msg name
101
+ #
102
+ # @param [String] filePath path to check - this can be a directory or file
103
+ def getSagaName( filePath )
104
+ baseName = File.basename( filePath )
105
+ extName = File.extname( baseName )
106
+
107
+ sagaName = baseName.sub( extName, "" )
108
+
109
+ return sagaName
110
+ end
111
+
112
+
113
+ #Entry point for loading Sagas
114
+ #
115
+ # @param [String] baseDir directory to check - should not have trailing slash
116
+ def loadSagasFromPath(baseDir)
117
+ self.getListOfFilesForDir(baseDir).each do |filePath|
118
+ if !filePath.end_with?( "." ) then
119
+
120
+ sagaName = self.getSagaName( filePath )
121
+ self.loadSaga( filePath, sagaName )
122
+ end
123
+ end
124
+
125
+ return self
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,21 @@
1
+ module RServiceBus
2
+
3
+ class SagaStorage
4
+
5
+ def SagaStorage.Get( uri )
6
+ case uri.scheme
7
+ when "dir"
8
+ require "rservicebus/SagaStorage/Dir"
9
+ return SagaStorage_Dir.new( uri )
10
+ when "inmem"
11
+ require "rservicebus/SagaStorage/InMemory"
12
+ return SagaStorage_InMemory.new( uri )
13
+ else
14
+ abort("Scheme, #{uri.scheme}, not recognised when configuring SagaStorage, #{uri.to_s}");
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end