mikka 1.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ /*.gem
2
+ /.bundle
3
+ /Gemfile.lock
4
+ /pkg/*
5
+ /ext
6
+ .DS_Store
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use jruby-1.6.3@mikka
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/README.mdown ADDED
@@ -0,0 +1,33 @@
1
+ # Mikka
2
+
3
+ Mikka is a thin Ruby wrapper around Akka. It makes Akka's Java API more pleasing to the Rubyist's eye.
4
+
5
+ ## Work in progress
6
+
7
+ Mikka is a work in progress, but currently used in production. It currently mostly scratches my own itchs, but improvement suggestions and patches are welcome.
8
+
9
+ Check out the `examples` directory for the basics. Start with the `hello_world.rb` example, it is extensively documented and goes through the basics.
10
+
11
+ If you make something that can serve as a useful illustration of some concept, please contribute by sending a patch.
12
+
13
+ ## Requirements
14
+
15
+ Only tested in JRuby 1.6.3 and Ruby 1.9 mode (add `jruby --1.9` or set `JRUBY_OPTS='--1.9`).
16
+
17
+ The required Akka and Scala JARs are loaded from the [akka-actor-jars](https://rubygems.org/gems/akka-actor-jars) and [scala-library-jars](https://rubygems.org/gems/scala-library-jars) wrapper gems.
18
+
19
+ ## Installation
20
+
21
+ gem install mikka
22
+
23
+ ## Contributors
24
+
25
+ Theo Hultberg, [@iconara](http://twitter.com/iconara)
26
+
27
+ ## License
28
+
29
+ Mikka is licensed under the Apache 2 license, the same as Akka. See http://akka.io/docs/akka/1.1.2/project/licenses.html
30
+
31
+ ## Mikka?
32
+
33
+ Mikka is a glacier close to the Akka massive.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler'
4
+
5
+
6
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'bundler/setup'
6
+ require 'mikka'
7
+
8
+ # 0.
9
+ # The numbers at the top of the comment blocks follow the flow of the
10
+ # application, read them in order to see the sequence of events.
11
+
12
+ # 1.
13
+ # The simplest way to create an actor is to do it with a block. The block
14
+ # will receive each message and execute in the scope of an actor object (so
15
+ # `context` will be available -- more on that later).
16
+ #
17
+ # The variable `phil` will not refer directly to an actor instance, but to an
18
+ # `ActorRef`. There are many reasons for this, and for the full picture refer
19
+ # to the Akka documentation. The short explanation is that it makes it
20
+ # possible to restart and replace the actor without the clients knowing that
21
+ # the actor instance was changed, and it makes remote actors transparent.
22
+ phil = Mikka.actor do |msg|
23
+ # 8.
24
+ # Ruby doesn't have pattern matching, but `case` is usually enough. If you
25
+ # Have separate classes for your messages you can match on class.
26
+ case msg
27
+ when Introduction
28
+ # 9.
29
+ # The `Introduction` class has a field called `to_whom` that contains a
30
+ # reference to an actor that someone want to introduce us to. Let's send
31
+ # that actor a `Greeting`.
32
+ msg.to_whom << Greeting.new('Hello, dear sir.')
33
+ when Greeting
34
+ puts "Received greeting: #{msg.body}"
35
+ # 12.
36
+ # The reply got routed back to this actor. Now we want to shut down this
37
+ # actor too, so that the application can shut down.
38
+ context.exit
39
+ end
40
+ end
41
+
42
+ # 2.
43
+ # If you need more control over your actor's life cycle, or want to create
44
+ # more than one of the same you can can declare a subclass of Mikka::Actor.
45
+ # The #receive method is called for each message the actor receives. You can
46
+ # do things when the actor starts by implementing #pre_start, and when the
47
+ # actor stops by implementing #post_stop.
48
+ #
49
+ # Don't create instances of this class yourself, an error will be raised if
50
+ # it's done in the wrong way, see below for how it's supposed to be done.
51
+ class Replier < Mikka::Actor
52
+ def receive(msg)
53
+ case msg
54
+ when Greeting
55
+ puts "Received greeting: #{msg.body}"
56
+ # 10.
57
+ # To reply to a message without having to explicitly refer to the sender
58
+ # use #reply (you can, however, store the sender and reply later if you
59
+ # want, it is available through `context.sender.get`).
60
+ #
61
+ # A word of caution: the sender is not always set, for example when the
62
+ # message was sent from a non-actor context like in 6. Also, the Scala
63
+ # API sets the sender implicitly in a way that is not possible in Java
64
+ # or Ruby, and even if Mikka does it's best to emulate the Scala way,
65
+ # it's not foolproof. If you want to guard agains errors when replying
66
+ # you can use #reply_safe instead of #reply. It will return true if the
67
+ # message could be sent, and false otherwise.
68
+ context.reply(Greeting.new('Why, hello old chap.'))
69
+ # 11.
70
+ # Now we're through with this actor, so we shut it down.
71
+ context.exit
72
+ end
73
+ end
74
+ end
75
+
76
+ # 3.
77
+ # This is how you create an actor reference from a subclass of Mikka::Actor.
78
+ # Creating the instance this way makes sure that the actor is set up correctly
79
+ # (refer to the Akka documentation as to why it works this way).
80
+ sam = Mikka.actor_of(Replier)
81
+
82
+ # 4.
83
+ # Messages can be anything, but if your messages are more complex than a
84
+ # simple string or symbol you are best off declaring a proper class to
85
+ # encapsulate them. `Struct` is convenient in this case (but creates mutable
86
+ # objects, consider using the `immutable_struct` gem instead).
87
+ class Introduction < Struct.new(:to_whom); end
88
+ class Greeting < Struct.new(:body); end
89
+
90
+ # 5.
91
+ # An actor must be started before it can be used!
92
+ phil.start
93
+ sam.start
94
+
95
+ # 6.
96
+ # And finally, this is how to send a message to an actor. In Erlang and Scala
97
+ # it is done with !, but that operator is not overridable in Ruby, so we have
98
+ # to make do with <<. The Akka Java API defines #send_one_way (actually
99
+ # sendOneWay, but JRuby fixes the casing for us), which can also be used.
100
+ #
101
+ # Here we send a message to `phil`, the message is an object that contains a
102
+ # reference to the actor `sam`.
103
+ phil << Introduction.new(sam)
104
+
105
+ # 7.
106
+ # The application will keep running until the last actor is dead. If you want
107
+ # to force shutdown you can use `Mikka.registry.shutdown_all`.
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'bundler/setup'
6
+ require 'mikka'
7
+
8
+
9
+ worker1 = Mikka.actor { |msg| puts "Worker 1 working on #{msg}" }
10
+ worker2 = Mikka.actor { |msg| puts "Worker 2 working on #{msg}" }
11
+ balancer = Mikka.load_balancer(:actors => [worker1, worker2])
12
+
13
+ 10.times do |i|
14
+ balancer << "item #{i}"
15
+ end
16
+
17
+ balancer << Mikka::Messages.broadcast("a message to everyone")
18
+ balancer << Mikka::Messages.poison_pill
19
+
20
+ Mikka.registry.shutdown_all
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'bundler/setup'
6
+ require 'mikka'
7
+
8
+
9
+ class Worker < Mikka::Actor
10
+ def receive(message)
11
+ puts "#{context.uuid} Work on #{message}"
12
+ end
13
+ end
14
+
15
+ balancer = Mikka.load_balancer(:type => Worker, :count => 4)
16
+
17
+ 10.times do |i|
18
+ balancer << "item #{i}"
19
+ end
20
+
21
+ balancer << Mikka::Messages.broadcast("a message to everyone")
22
+
23
+ Mikka.registry.shutdown_all
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'bundler/setup'
6
+ require 'mikka'
7
+
8
+
9
+ actor = Mikka.actor do |message|
10
+ case message
11
+ when 'hi'
12
+ puts "hello yourself"
13
+ when 'goodbye'
14
+ puts "adieu"
15
+ context.exit
16
+ else
17
+ puts "sorry, come again?"
18
+ end
19
+ end
20
+
21
+ actor.start
22
+ actor << 'hi'
23
+ actor << 'hello'
24
+ actor << 'goodbye'
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'bundler/setup'
6
+ require 'mikka'
7
+
8
+
9
+ class Stallone < Mikka::Actor
10
+ def receive(message)
11
+ context.exit if message == 'punch!'
12
+ end
13
+ end
14
+
15
+ stallone = Mikka.actor_of(Stallone).start
16
+ stallone << 'punch!'
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'bundler/setup'
6
+ require 'mikka'
7
+
8
+
9
+ class Worker < Mikka::Actor
10
+ life_cycle :permanent
11
+
12
+ def pre_start
13
+ puts "#{context.id} starting"
14
+ end
15
+
16
+ def pre_restart(reason)
17
+ puts "#{context.id} restarting"
18
+ end
19
+
20
+ def post_restart(reason)
21
+ puts "#{context.id} restarted"
22
+ end
23
+
24
+ def post_stop
25
+ puts "#{context.id} stopped"
26
+ end
27
+
28
+ def receive(message)
29
+ raise java.lang.Exception.new('Oh, shucks') if message == 'hard work'
30
+ puts "#{context.id} Work on #{message}"
31
+ end
32
+ end
33
+
34
+ class Manager < Mikka::Actor
35
+ fault_handling :strategy => :all_for_one, :trap => [java.lang.Exception], :max_retries => 3, :time_range => 3000
36
+
37
+ def pre_start
38
+ @worker1 = Mikka.actor_of { Worker.new }
39
+ @worker2 = Mikka.actor_of { Worker.new }
40
+ @worker1.id = 'worker1'
41
+ @worker2.id = 'worker2'
42
+ context.start_link(@worker1)
43
+ context.start_link(@worker2)
44
+ @worker1 << 'simple work'
45
+ @worker2 << 'hard work'
46
+ @worker2 << 'simple work'
47
+ @worker2 << 'simple work'
48
+ @worker2 << 'hard work'
49
+ @worker2 << 'simple work'
50
+ @worker2 << 'simple work'
51
+ @worker2 << 'simple work'
52
+ end
53
+ end
54
+
55
+ manager = Mikka.actor_of(Manager).start
data/lib/akka.rb ADDED
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'java'
4
+ require 'akka-actor-jars'
5
+
6
+
7
+ module Akka
8
+ module Actor
9
+ include_package 'akka.actor'
10
+
11
+ import 'akka.actor.ActorRef'
12
+ import 'akka.actor.UntypedActor'
13
+
14
+ class UntypedActor
15
+ def self.create(*args)
16
+ new(*args)
17
+ end
18
+ end
19
+ end
20
+
21
+ module Config
22
+ include_package 'akka.config'
23
+ end
24
+
25
+ module Routing
26
+ include_package 'akka.routing'
27
+ end
28
+ end
data/lib/mikka.rb ADDED
@@ -0,0 +1,161 @@
1
+ # encoding: utf-8
2
+
3
+ require 'java'
4
+ require 'akka'
5
+
6
+
7
+ module Mikka
8
+ import java.util.Arrays
9
+
10
+ def self.actor_of(*args, &block)
11
+ Akka::Actor::Actors.actor_of(*args, &block)
12
+ end
13
+
14
+ def self.actor(&block)
15
+ Akka::Actor::Actors.actor_of { ProcActor.new(&block) }
16
+ end
17
+
18
+ def self.registry
19
+ Akka::Actor::Actors.registry
20
+ end
21
+
22
+ def self.current_actor
23
+ Thread.current[:mikka_current_actor]
24
+ end
25
+
26
+ module Messages
27
+ def self.broadcast(message)
28
+ Akka::Routing::Routing::Broadcast.new(message)
29
+ end
30
+
31
+ def self.poison_pill
32
+ Akka::Actor::Actors.poison_pill
33
+ end
34
+ end
35
+
36
+ module RubyesqueActorCallbacks
37
+ def receive(message); end
38
+ def pre_start; end
39
+ def post_stop; end
40
+ def pre_restart(reason); end
41
+ def post_restart(reason); end
42
+
43
+ def onReceive(message); receive(message); end
44
+ def preStart; super; pre_start; end
45
+ def postStop; super; post_stop; end
46
+ def preRestart(reason); super; pre_restart(reason); end
47
+ def postRestart(reason); super; post_restart(reason); end
48
+ end
49
+
50
+ module ImplicitSender
51
+ def capture_current_actor
52
+ Thread.current[:mikka_current_actor] = context
53
+ yield
54
+ ensure
55
+ Thread.current[:mikka_current_actor] = nil
56
+ end
57
+
58
+ def self.included(mod)
59
+ mod.class_eval do
60
+ [:onReceive, :preStart, :postStop, :preRestart, :postRestart].each do |method_name|
61
+ actual_method_name = :"__actual_#{method_name}"
62
+ alias_method actual_method_name, method_name
63
+ define_method method_name do |*args|
64
+ capture_current_actor do
65
+ send(actual_method_name, *args)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ module SupervisionDsl
74
+ module ClassMethods
75
+ def fault_handling(config)
76
+ trap = config[:trap].map { |e| e.java_class }
77
+ max_retries = config.fetch(:max_retries, 5)
78
+ time_range = config.fetch(:time_range, 5000)
79
+ case config[:strategy]
80
+ when :all_for_one
81
+ @fault_handling_strategy = Akka::Config::Supervision::AllForOneStrategy.new(trap, max_retries, time_range)
82
+ when :one_for_one
83
+ @fault_handling_strategy = Akka::Config::Supervision::OneForOneStrategy.new(trap, max_retries, time_range)
84
+ else
85
+ raise ArgumentError, 'strategy must be one of :all_for_one or :one_for_one'
86
+ end
87
+ end
88
+
89
+ def registered_fault_handling_strategy
90
+ @fault_handling_strategy
91
+ end
92
+
93
+ def life_cycle(type)
94
+ @life_cycle = case type
95
+ when :permanent then Akka::Config::Supervision.permanent
96
+ when :temporary then Akka::Config::Supervision.temporary
97
+ when :undefined then Akka::Config::Supervision.undefined_life_cycle
98
+ else raise ArgumentError, 'type must be one of :permanent, :temporary or :undefined'
99
+ end
100
+ end
101
+
102
+ def registered_life_cycle
103
+ @life_cycle
104
+ end
105
+ end
106
+
107
+ module InstanceMethods
108
+ def initialize(*args)
109
+ super
110
+ if self.class.registered_fault_handling_strategy
111
+ context.fault_handler = self.class.registered_fault_handling_strategy
112
+ end
113
+ if self.class.registered_life_cycle
114
+ context.life_cycle = self.class.registered_life_cycle
115
+ end
116
+ end
117
+ end
118
+
119
+ def self.included(m)
120
+ m.extend(ClassMethods)
121
+ m.include(InstanceMethods)
122
+ end
123
+ end
124
+
125
+ class Actor < Akka::Actor::UntypedActor
126
+ include SupervisionDsl
127
+ include RubyesqueActorCallbacks
128
+ include ImplicitSender
129
+ end
130
+
131
+ class ProcActor < Actor
132
+ def initialize(&receive)
133
+ define_singleton_method(:receive, receive)
134
+ end
135
+ end
136
+
137
+ def self.load_balancer(options={})
138
+ actors = options[:actors]
139
+ unless actors
140
+ type = options[:type]
141
+ count = options[:count]
142
+ raise ArgumentError, "Either :actors or :type and :count must be specified" unless type && count
143
+ actors = (0...count).map { actor_of(type) }
144
+ end
145
+ actors.each { |a| a.start }
146
+ actor_list = Arrays.as_list(actors.to_java)
147
+ actor_seq = Akka::Routing::CyclicIterator.new(actor_list)
148
+ actor_factory = proc { actor_seq }.to_function
149
+ Akka::Routing::Routing.load_balancer_actor(actor_factory)
150
+ end
151
+ end
152
+
153
+ module Akka
154
+ module Actor
155
+ module ActorRef
156
+ def <<(message)
157
+ send_one_way(message, Mikka.current_actor)
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module Mikka
4
+ VERSION = '1.0.1'
5
+ end
data/mikka.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../lib', __FILE__)
4
+
5
+ require 'mikka/version'
6
+
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'mikka'
10
+ s.version = Mikka::VERSION
11
+ s.platform = 'java'
12
+ s.authors = ['Theo Hultberg']
13
+ s.email = ['theo@iconara.net']
14
+ s.homepage = 'http://github.com/iconara/mikka'
15
+ s.summary = %q{Mikka is a JRuby wrapper for Akka}
16
+ s.description = %q{Mikka adapts Akka's Java API to fit better with Ruby}
17
+
18
+ s.rubyforge_project = 'mikka'
19
+
20
+ s.add_dependency 'akka-actor-jars', '~> 1.1.0'
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ # s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ # s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = %w(lib)
26
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mikka
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 1
9
+ version: 1.0.1
10
+ platform: java
11
+ authors:
12
+ - Theo Hultberg
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-08-07 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: akka-actor-jars
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 1
30
+ - 0
31
+ version: 1.1.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: Mikka adapts Akka's Java API to fit better with Ruby
35
+ email:
36
+ - theo@iconara.net
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - .gitignore
45
+ - .rvmrc
46
+ - Gemfile
47
+ - README.mdown
48
+ - Rakefile
49
+ - examples/hello_world.rb
50
+ - examples/load_balancing1.rb
51
+ - examples/load_balancing2.rb
52
+ - examples/proc_example.rb
53
+ - examples/simple_example.rb
54
+ - examples/supervision1.rb
55
+ - lib/akka.rb
56
+ - lib/mikka.rb
57
+ - lib/mikka/version.rb
58
+ - mikka.gemspec
59
+ has_rdoc: true
60
+ homepage: http://github.com/iconara/mikka
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options: []
65
+
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project: mikka
85
+ rubygems_version: 1.3.6
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Mikka is a JRuby wrapper for Akka
89
+ test_files: []
90
+