redstorm 0.6.4 → 0.6.5

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.
Files changed (43) hide show
  1. data/CHANGELOG.md +9 -0
  2. data/README.md +206 -103
  3. data/examples/native/cluster_word_count_topology.rb +5 -5
  4. data/examples/native/local_exclamation_topology.rb +8 -8
  5. data/examples/native/local_exclamation_topology2.rb +7 -7
  6. data/examples/native/local_redis_word_count_topology.rb +7 -8
  7. data/examples/native/local_word_count_topology.rb +5 -5
  8. data/examples/simple/exclamation_topology.rb +7 -11
  9. data/examples/simple/exclamation_topology2.rb +10 -12
  10. data/examples/simple/hello_world_topology.rb +22 -0
  11. data/examples/simple/kafka_topology.rb +2 -1
  12. data/examples/simple/redis_word_count_topology.rb +3 -5
  13. data/examples/simple/ruby_version_topology.rb +7 -1
  14. data/examples/simple/word_count_topology.rb +8 -10
  15. data/ivy/settings.xml +1 -0
  16. data/ivy/storm_dependencies.xml +8 -0
  17. data/ivy/topology_dependencies.xml +7 -0
  18. data/lib/red_storm.rb +1 -0
  19. data/lib/red_storm/application.rb +9 -7
  20. data/lib/red_storm/configurator.rb +1 -1
  21. data/lib/red_storm/proxy/batch_bolt.rb +63 -0
  22. data/lib/red_storm/proxy/batch_committer_bolt.rb +52 -0
  23. data/lib/red_storm/proxy/batch_spout.rb +59 -0
  24. data/lib/red_storm/proxy/proxy_function.rb +40 -0
  25. data/lib/red_storm/proxy/transactional_committer_spout.rb +47 -0
  26. data/lib/red_storm/proxy/transactional_spout.rb +46 -0
  27. data/lib/red_storm/simple_drpc_topology.rb +87 -0
  28. data/lib/red_storm/simple_topology.rb +14 -4
  29. data/lib/red_storm/topology_launcher.rb +22 -3
  30. data/lib/red_storm/version.rb +1 -1
  31. data/lib/tasks/red_storm.rake +66 -104
  32. data/redstorm.gemspec +24 -0
  33. data/src/main/redstorm/storm/jruby/JRubyBatchBolt.java +90 -0
  34. data/src/main/redstorm/storm/jruby/JRubyBatchCommitterBolt.java +9 -0
  35. data/src/main/redstorm/storm/jruby/JRubyBatchSpout.java +88 -0
  36. data/src/main/redstorm/storm/jruby/JRubyProxyFunction.java +51 -0
  37. data/src/main/redstorm/storm/jruby/JRubyShellSpout.java +1 -1
  38. data/src/main/redstorm/storm/jruby/JRubyTransactionalBolt.java +90 -0
  39. data/src/main/redstorm/storm/jruby/JRubyTransactionalCommitterBolt.java +31 -0
  40. data/src/main/redstorm/storm/jruby/JRubyTransactionalCommitterSpout.java +44 -0
  41. data/src/main/redstorm/storm/jruby/JRubyTransactionalSpout.java +89 -0
  42. metadata +35 -14
  43. data/examples/native/Gemfile +0 -2
@@ -1,23 +1,37 @@
1
1
  require 'java'
2
2
 
3
+ # This hack get rif of the "Use RbConfig instead of obsolete and deprecated Config"
4
+ # deprecation warning that is triggered by "java_import 'backtype.storm.Config'".
5
+ Object.send :remove_const, :Config
6
+ Config = RbConfig
7
+
3
8
  # see https://github.com/colinsurprenant/redstorm/issues/7
4
9
  module Backtype
5
10
  java_import 'backtype.storm.Config'
6
11
  end
7
12
 
8
13
  java_import 'backtype.storm.LocalCluster'
14
+ java_import 'backtype.storm.LocalDRPC'
9
15
  java_import 'backtype.storm.StormSubmitter'
10
16
  java_import 'backtype.storm.topology.TopologyBuilder'
17
+ java_import 'backtype.storm.coordination.BatchBoltExecutor'
18
+ java_import 'backtype.storm.drpc.LinearDRPCTopologyBuilder'
11
19
  java_import 'backtype.storm.tuple.Fields'
12
20
  java_import 'backtype.storm.tuple.Tuple'
13
21
  java_import 'backtype.storm.tuple.Values'
14
22
 
15
23
  java_import 'redstorm.storm.jruby.JRubyBolt'
16
24
  java_import 'redstorm.storm.jruby.JRubySpout'
25
+ java_import 'redstorm.storm.jruby.JRubyBatchBolt'
26
+ java_import 'redstorm.storm.jruby.JRubyBatchCommitterBolt'
27
+ java_import 'redstorm.storm.jruby.JRubyBatchSpout'
28
+ java_import 'redstorm.storm.jruby.JRubyTransactionalSpout'
29
+ java_import 'redstorm.storm.jruby.JRubyTransactionalBolt'
30
+ java_import 'redstorm.storm.jruby.JRubyTransactionalCommitterBolt'
17
31
 
18
32
  java_package 'redstorm'
19
33
 
20
- # TopologyLauncher is the application entry point when launching a topology. Basically it will
34
+ # TopologyLauncher is the application entry point when launching a topology. Basically it will
21
35
  # call require on the specified Ruby topology class file path and call its start method
22
36
  class TopologyLauncher
23
37
 
@@ -36,14 +50,19 @@ class TopologyLauncher
36
50
  $:.unshift File.expand_path(launch_path + '/lib')
37
51
  $:.unshift File.expand_path(launch_path + '/target/lib')
38
52
 
39
- require "#{class_path}"
53
+ require "#{class_path}"
54
+
55
+ if RedStorm::Configuration.topology_class.nil? || !RedStorm::Configuration.topology_class.method_defined?(:start)
56
+ puts("\nERROR: invalid topology class. make sure your topology class is a subclass of one of the DSL topology classes or that your class sets RedStorm::Configuration.topology_class and defines the start method\n\n")
57
+ exit(1)
58
+ end
40
59
 
41
60
  topology_name = RedStorm::Configuration.topology_class.respond_to?(:topology_name) ? "/#{RedStorm::Configuration.topology_class.topology_name}" : ''
42
61
  puts("RedStorm v#{RedStorm::VERSION} starting topology #{RedStorm::Configuration.topology_class.name}#{topology_name} in #{env.to_s} environment")
43
62
  RedStorm::Configuration.topology_class.new.start(class_path, env)
44
63
  end
45
64
 
46
- private
65
+ private
47
66
 
48
67
  def self.camel_case(s)
49
68
  s.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
@@ -1,3 +1,3 @@
1
1
  module RedStorm
2
- VERSION = '0.6.4'
2
+ VERSION = '0.6.5'
3
3
  end
@@ -1,7 +1,7 @@
1
1
  begin
2
2
  require 'ant'
3
3
  rescue
4
- puts("error: unable to load Ant, make sure Ant is installed and in your PATH")
4
+ puts("ERROR: unable to load Ant, make sure Ant is installed, in your PATH and $ANT_HOME is defined properly")
5
5
  puts("\nerror detail:\n#{$!}")
6
6
  exit(1)
7
7
  end
@@ -10,18 +10,7 @@ require 'jruby/jrubyc'
10
10
  require 'red_storm'
11
11
  require 'red_storm/application'
12
12
 
13
- DEP_STORM_VERSION = "0.8.1"
14
- DEP_JRUBY_VERSION = "1.6.8"
15
- INSTALL_IVY_VERSION = "2.2.0"
16
-
17
- DEFAULT_DEPENDENCIES = {
18
- :storm_artifacts => [
19
- "storm:storm:#{DEP_STORM_VERSION}, transitive=true",
20
- ],
21
- :topology_artifacts => [
22
- "org.jruby:jruby-complete:#{DEP_JRUBY_VERSION}, transitive=false",
23
- ],
24
- }
13
+ INSTALL_IVY_VERSION = "2.3.0"
25
14
 
26
15
  task :launch, :env, :ruby_mode, :class_file do |t, args|
27
16
  # use ruby mode parameter or default to current interpreter version
@@ -32,7 +21,7 @@ task :launch, :env, :ruby_mode, :class_file do |t, args|
32
21
  RedStorm::Application.local_storm_command(args[:class_file], args[:ruby_mode])
33
22
  when "cluster"
34
23
  unless File.exist?(TARGET_CLUSTER_JAR)
35
- puts("error: cluster jar file #{TARGET_CLUSTER_JAR} not found. Generate it usingw $redstorm jar DIR1 [DIR2, ...]")
24
+ puts("error: cluster jar file #{TARGET_CLUSTER_JAR} not found. Generate it using $redstorm jar DIR1 [DIR2, ...]")
36
25
  exit(1)
37
26
  end
38
27
  RedStorm::Application.cluster_storm_command(args[:class_file], args[:ruby_mode])
@@ -45,24 +34,24 @@ task :launch, :env, :ruby_mode, :class_file do |t, args|
45
34
  end
46
35
 
47
36
  task :clean do
48
- ant.delete :dir => TARGET_DIR
37
+ ant.delete 'dir' => TARGET_DIR
49
38
  end
50
39
 
51
40
  task :clean_jar do
52
- ant.delete :file => TARGET_CLUSTER_JAR
41
+ ant.delete 'file' => TARGET_CLUSTER_JAR
53
42
  end
54
43
 
55
44
  task :setup do
56
45
  puts("\n--> Setting up target directories")
57
- ant.mkdir :dir => TARGET_DIR
58
- ant.mkdir :dir => TARGET_CLASSES_DIR
59
- ant.mkdir :dir => TARGET_DEPENDENCY_DIR
60
- ant.mkdir :dir => TARGET_SRC_DIR
61
- ant.mkdir :dir => TARGET_GEM_DIR
62
- ant.mkdir :dir => TARGET_SPECS_DIR
63
- ant.path :id => 'classpath' do
64
- fileset :dir => TARGET_DEPENDENCY_DIR
65
- fileset :dir => TARGET_CLASSES_DIR
46
+ ant.mkdir 'dir' => TARGET_DIR
47
+ ant.mkdir 'dir' => TARGET_CLASSES_DIR
48
+ ant.mkdir 'dir' => TARGET_DEPENDENCY_DIR
49
+ ant.mkdir 'dir' => TARGET_SRC_DIR
50
+ ant.mkdir 'dir' => TARGET_GEM_DIR
51
+ ant.mkdir 'dir' => TARGET_SPECS_DIR
52
+ ant.path 'id' => 'classpath' do
53
+ fileset 'dir' => TARGET_DEPENDENCY_DIR
54
+ fileset 'dir' => TARGET_CLASSES_DIR
66
55
  end
67
56
  end
68
57
 
@@ -111,18 +100,23 @@ task :bundle, [:groups] => :setup do |t, args|
111
100
  defaulted_args = {:groups => 'default'}.merge(args.to_hash.delete_if{|k, v| v.to_s.empty?})
112
101
  groups = defaulted_args[:groups].split(':').map(&:to_sym)
113
102
  Bundler.definition.specs_for(groups).each do |spec|
114
- unless spec.full_name =~ /^bundler-\d+/
115
- destination_path = "#{TARGET_GEM_DIR}/#{spec.full_name}"
116
- unless File.directory?(destination_path)
117
- puts("installing gem #{spec.full_name} into #{destination_path}")
118
- # copy the actual gem dir
119
- FileUtils.cp_r(spec.full_gem_path, destination_path)
120
- # copy the gemspec into the specifications/ dir
121
- FileUtils.cp_r(spec.loaded_from, TARGET_SPECS_DIR)
122
- # strip the .git directory from git dependencies, it can be huge
123
- FileUtils.rm_rf("#{destination_path}/.git")
124
- end
125
- end
103
+ next if spec.name == 'bundler'
104
+
105
+ # try to avoid infinite recursion
106
+ next if TARGET_GEM_DIR.start_with?(spec.full_gem_path)
107
+
108
+ destination_path = "#{TARGET_GEM_DIR}/#{spec.full_name}"
109
+ next if File.directory?(destination_path)
110
+
111
+ puts("installing gem #{spec.full_name} into #{destination_path}")
112
+ # copy the actual gem dir
113
+ FileUtils.cp_r(spec.full_gem_path, destination_path)
114
+ # copy the evaluated gemspec into the specifications/ dir (we
115
+ # may not have enough info to reconstruct once we delete the
116
+ # .git directory)
117
+ File.open(File.join(TARGET_SPECS_DIR, File.basename(spec.loaded_from)), 'w'){|f| f.write(spec.to_ruby)}
118
+ # strip the .git directory from git dependencies, it can be huge
119
+ FileUtils.rm_rf("#{destination_path}/.git")
126
120
  end
127
121
  end
128
122
 
@@ -130,21 +124,21 @@ namespace :ivy do
130
124
  task :download do
131
125
  mkdir_p DST_IVY_DIR
132
126
  ant.get({
133
- :src => "http://repo1.maven.org/maven2/org/apache/ivy/ivy/#{INSTALL_IVY_VERSION}/ivy-#{INSTALL_IVY_VERSION}.jar",
134
- :dest => "#{DST_IVY_DIR}/ivy-#{INSTALL_IVY_VERSION}.jar",
135
- :usetimestamp => true,
127
+ 'src' => "http://repo1.maven.org/maven2/org/apache/ivy/ivy/#{INSTALL_IVY_VERSION}/ivy-#{INSTALL_IVY_VERSION}.jar",
128
+ 'dest' => "#{DST_IVY_DIR}/ivy-#{INSTALL_IVY_VERSION}.jar",
129
+ 'usetimestamp' => true,
136
130
  })
137
131
  end
138
132
 
139
133
  task :install => :download do
140
- ant.path :id => 'ivy.lib.path' do
141
- fileset :dir => DST_IVY_DIR, :includes => '*.jar'
134
+ ant.path 'id' => 'ivy.lib.path' do
135
+ fileset 'dir' => DST_IVY_DIR, 'includes' => '*.jar'
142
136
  end
143
137
 
144
138
  ant.taskdef({
145
- :resource => "org/apache/ivy/ant/antlib.xml",
146
- :classpathref => "ivy.lib.path",
147
- #:uri => "antlib:org.apache.ivy.ant",
139
+ 'resource' => "org/apache/ivy/ant/antlib.xml",
140
+ 'classpathref' => "ivy.lib.path",
141
+ #'uri' => "antlib:org.apache.ivy.ant",
148
142
  })
149
143
  end
150
144
  end
@@ -152,37 +146,32 @@ end
152
146
  task :deps => "ivy:install" do
153
147
  puts("\n--> Installing dependencies")
154
148
 
155
- dependencies = File.exists?(CUSTOM_DEPENDENCIES) ? eval(File.read(CUSTOM_DEPENDENCIES)) : DEFAULT_DEPENDENCIES
156
- ant.configure :file => File.exists?(CUSTOM_IVY_SETTINGS) ? CUSTOM_IVY_SETTINGS : DEFAULT_IVY_SETTINGS
149
+ ant.configure 'file' => File.exists?(CUSTOM_IVY_SETTINGS) ? CUSTOM_IVY_SETTINGS : DEFAULT_IVY_SETTINGS
157
150
 
158
- dependencies[:storm_artifacts].each do |dependency|
159
- artifact, transitive = dependency.split(/\s*,\s*/)
160
- ivy_retrieve(*artifact.split(':').concat([transitive.split(/\s*=\s*/).last, "#{TARGET_DEPENDENCY_DIR}/storm", "default"]))
161
- end
151
+ ant.resolve 'file' => File.exists?(CUSTOM_IVY_STORM_DEPENDENCIES) ? CUSTOM_IVY_STORM_DEPENDENCIES : DEFAULT_IVY_STORM_DEPENDENCIES
152
+ ant.retrieve 'pattern' => "#{TARGET_DEPENDENCY_DIR}/storm/[conf]/[artifact]-[revision].[ext]", 'sync' => "true"
162
153
 
163
- dependencies[:topology_artifacts].each do |dependency|
164
- artifact, transitive = dependency.split(/\s*,\s*/)
165
- ivy_retrieve(*artifact.split(':').concat([transitive.split(/\s*=\s*/).last, "#{TARGET_DEPENDENCY_DIR}/topology", "default"]))
166
- end
167
- end
154
+ ant.resolve 'file' => File.exists?(CUSTOM_IVY_TOPOLOGY_DEPENDENCIES) ? CUSTOM_IVY_TOPOLOGY_DEPENDENCIES : DEFAULT_IVY_TOPOLOGY_DEPENDENCIES
155
+ ant.retrieve 'pattern' => "#{TARGET_DEPENDENCY_DIR}/topology/[conf]/[artifact]-[revision].[ext]", 'sync' => "true"
156
+ end
168
157
 
169
158
  task :jar, [:include_dir] => [:clean_jar] do |t, args|
170
159
  puts("\n--> Generating JAR file #{TARGET_CLUSTER_JAR}")
171
160
 
172
- ant.jar :destfile => TARGET_CLUSTER_JAR do
161
+ ant.jar 'destfile' => TARGET_CLUSTER_JAR do
173
162
  # rejar all topology jars
174
163
  Dir["target/dependency/topology/default/*.jar"].each do |jar|
175
164
  puts("Extracting #{jar}")
176
- zipfileset :src => jar, :includes => "**/*"
165
+ zipfileset 'src' => jar, 'includes' => "**/*"
177
166
  end
178
- fileset :dir => TARGET_DIR do
179
- include :name => "gems/**"
167
+ fileset 'dir' => TARGET_DIR do
168
+ include 'name' => "gems/**"
180
169
  end
181
- fileset :dir => TARGET_CLASSES_DIR
170
+ fileset 'dir' => TARGET_CLASSES_DIR
182
171
  # red_storm.rb and red_storm/* must be in root of jar so that "require 'red_storm'"
183
172
  # in bolts/spouts works in jar context
184
- fileset :dir => TARGET_LIB_DIR do
185
- exclude :name => "tasks/**"
173
+ fileset 'dir' => TARGET_LIB_DIR do
174
+ exclude 'name' => "tasks/**"
186
175
  end
187
176
  if args[:include_dir]
188
177
  dirs = args[:include_dir].split(":")
@@ -192,19 +181,19 @@ task :jar, [:include_dir] => [:clean_jar] do |t, args|
192
181
  resources_dirs = Dir.glob("#{dir}/**/resources")
193
182
  resources_dirs.each do |resources_dir|
194
183
  resources_parent = resources_dir.gsub("/resources", "")
195
- fileset :dir => resources_parent do
196
- include :name => "resources/**/*"
184
+ fileset 'dir' => resources_parent do
185
+ include 'name' => "resources/**/*"
197
186
  end
198
187
  end
199
188
  end
200
189
 
201
190
  # include complete source dir tree (note we don't care about potential duplicated resources dir)
202
- fileset :dir => CWD do
203
- dirs.each{|dir| include :name => "#{dir}/**/*"}
191
+ fileset 'dir' => CWD do
192
+ dirs.each{|dir| include 'name' => "#{dir}/**/*"}
204
193
  end
205
194
  end
206
195
  manifest do
207
- attribute :name => "Main-Class", :value => "redstorm.TopologyLauncher"
196
+ attribute 'name' => "Main-Class", 'value' => "redstorm.TopologyLauncher"
208
197
  end
209
198
  end
210
199
  puts("\nRedStorm generated JAR file #{TARGET_CLUSTER_JAR}")
@@ -213,15 +202,15 @@ end
213
202
  def build_java_dir(source_folder)
214
203
  puts("\n--> Compiling Java")
215
204
  ant.javac(
216
- :srcdir => source_folder,
217
- :destdir => TARGET_CLASSES_DIR,
218
- :classpathref => 'classpath',
219
- :source => "1.6",
220
- :target => "1.6",
221
- :debug => "yes",
222
- :includeantruntime => "no",
223
- :verbose => false,
224
- :listfiles => true
205
+ 'srcdir' => source_folder,
206
+ 'destdir' => TARGET_CLASSES_DIR,
207
+ 'classpathref' => 'classpath',
208
+ 'source' => "1.7",
209
+ 'target' => "1.7",
210
+ 'debug' => "yes",
211
+ 'includeantruntime' => "no",
212
+ 'verbose' => false,
213
+ 'listfiles' => true
225
214
  ) do
226
215
  # compilerarg :value => "-Xlint:unchecked"
227
216
  end
@@ -242,30 +231,3 @@ def build_jruby(source_path)
242
231
  status = JRuby::Compiler::compile_argv(argv)
243
232
  end
244
233
  end
245
-
246
- def truefalse(s)
247
- return true if s.to_s.downcase =~ /1|yes|true/
248
- return false if s.to_s.downcase =~ /0|no|false/
249
- nil
250
- end
251
-
252
- def ivy_retrieve(org, mod, rev, transitive, dir, conf)
253
- ant.resolve({
254
- :organisation => org,
255
- :module => mod,
256
- :revision => rev,
257
- :inline => true,
258
- :transitive => truefalse(transitive),
259
- :conf => conf,
260
- })
261
-
262
- ant.retrieve({
263
- :organisation => org,
264
- :module => mod,
265
- :revision => rev,
266
- :pattern => "#{dir}/[conf]/[artifact]-[revision].[ext]",
267
- :inline => true,
268
- :transitive => truefalse(transitive),
269
- :conf => conf,
270
- })
271
- end
@@ -0,0 +1,24 @@
1
+ libdir = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift libdir unless $:.include?(libdir)
3
+
4
+ require 'red_storm/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'redstorm'
8
+ s.version = RedStorm::VERSION
9
+ s.authors = ['Colin Surprenant']
10
+ s.email = ['colin.surprenant@gmail.com']
11
+ s.homepage = 'https://github.com/colinsurprenant/redstorm'
12
+ s.summary = 'JRuby on Storm'
13
+ s.description = 'JRuby integration & DSL for the Storm distributed realtime computation system'
14
+
15
+ s.rubyforge_project = 'redstorm'
16
+
17
+ s.files = Dir.glob("{lib/**/*}") + Dir.glob("{ivy/*.xml}") + Dir.glob("{examples/**/*}") + Dir.glob("{src/**/*.java}") + Dir.glob("{bin/**/*}") + %w(redstorm.gemspec Rakefile README.md CHANGELOG.md LICENSE.md)
18
+ s.require_paths = ['lib']
19
+ s.bindir = 'bin'
20
+ s.executables = ['redstorm']
21
+
22
+ s.add_development_dependency 'rspec', '~> 2.11.0'
23
+ s.add_runtime_dependency 'rake'
24
+ end
@@ -0,0 +1,90 @@
1
+ package redstorm.storm.jruby;
2
+
3
+ import backtype.storm.task.OutputCollector;
4
+ import backtype.storm.task.TopologyContext;
5
+ import backtype.storm.topology.base.BaseBatchBolt;
6
+ import backtype.storm.coordination.BatchOutputCollector;
7
+ import backtype.storm.coordination.IBatchBolt;
8
+ import backtype.storm.topology.OutputFieldsDeclarer;
9
+ import backtype.storm.tuple.Tuple;
10
+ import backtype.storm.tuple.Fields;
11
+ import java.util.Map;
12
+
13
+ /**
14
+ * the JRubyBolt class is a simple proxy class to the actual bolt implementation in JRuby.
15
+ * this proxy is required to bypass the serialization/deserialization process when dispatching
16
+ * the bolts to the workers. JRuby does not yet support serialization from Java
17
+ * (Java serialization call on a JRuby class).
18
+ *
19
+ * Note that the JRuby bolt proxy class is instanciated in the prepare method which is called after
20
+ * deserialization at the worker and in the declareOutputFields method which is called once before
21
+ * serialization at topology creation.
22
+ */
23
+ public class JRubyBatchBolt extends BaseBatchBolt {
24
+ IBatchBolt _proxyBolt;
25
+ String _realBoltClassName;
26
+ String _baseClassPath;
27
+ String[] _fields;
28
+
29
+ /**
30
+ * create a new JRubyBolt
31
+ *
32
+ * @param baseClassPath the topology/project base JRuby class file path
33
+ * @param realBoltClassName the fully qualified JRuby bolt implementation class name
34
+ */
35
+ public JRubyBatchBolt(String baseClassPath, String realBoltClassName, String[] fields) {
36
+ _baseClassPath = baseClassPath;
37
+ _realBoltClassName = realBoltClassName;
38
+ _fields = fields;
39
+ }
40
+
41
+ @Override
42
+ public void prepare(final Map stormConf, final TopologyContext context, final BatchOutputCollector collector, final Object id) {
43
+ // create instance of the jruby class here, after deserialization in the workers.
44
+ _proxyBolt = newProxyBolt(_baseClassPath, _realBoltClassName);
45
+ _proxyBolt.prepare(stormConf, context, collector, id);
46
+ }
47
+
48
+ @Override
49
+ public void execute(Tuple input) {
50
+ _proxyBolt.execute(input);
51
+ }
52
+
53
+ @Override
54
+ public void finishBatch() {
55
+ _proxyBolt.finishBatch();
56
+ }
57
+
58
+ @Override
59
+ public void declareOutputFields(OutputFieldsDeclarer declarer) {
60
+ // declareOutputFields is executed in the topology creation time, before serialisation.
61
+ // do not set the _proxyBolt instance variable here to avoid JRuby serialization
62
+ // issues. Just create tmp bolt instance to call declareOutputFields.
63
+ if (_fields.length > 0) {
64
+ declarer.declare(new Fields(_fields));
65
+ } else {
66
+ IBatchBolt bolt = newProxyBolt(_baseClassPath, _realBoltClassName);
67
+ bolt.declareOutputFields(declarer);
68
+ }
69
+ }
70
+
71
+ @Override
72
+ public Map<String, Object> getComponentConfiguration() {
73
+ // getComponentConfiguration is executed in the topology creation time, before serialisation.
74
+ // do not set the _proxyBolt instance variable here to avoid JRuby serialization
75
+ // issues. Just create tmp bolt instance to call declareOutputFields.
76
+ IBatchBolt bolt = newProxyBolt(_baseClassPath, _realBoltClassName);
77
+ return bolt.getComponentConfiguration();
78
+ }
79
+
80
+
81
+ private static IBatchBolt newProxyBolt(String baseClassPath, String realBoltClassName) {
82
+ try {
83
+ redstorm.proxy.BatchBolt proxy = new redstorm.proxy.BatchBolt(baseClassPath, realBoltClassName);
84
+ return proxy;
85
+ }
86
+ catch (Exception e) {
87
+ throw new RuntimeException(e);
88
+ }
89
+ }
90
+ }