kb-redstorm 0.6.4
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.
- data/CHANGELOG.md +74 -0
- data/LICENSE.md +13 -0
- data/README.md +375 -0
- data/Rakefile +11 -0
- data/bin/redstorm +15 -0
- data/examples/native/Gemfile +2 -0
- data/examples/native/cluster_word_count_topology.rb +25 -0
- data/examples/native/exclamation_bolt.rb +21 -0
- data/examples/native/local_exclamation_topology.rb +31 -0
- data/examples/native/local_exclamation_topology2.rb +48 -0
- data/examples/native/local_redis_word_count_topology.rb +69 -0
- data/examples/native/local_word_count_topology.rb +27 -0
- data/examples/native/random_sentence_spout.rb +30 -0
- data/examples/native/split_sentence_bolt.rb +20 -0
- data/examples/native/word_count_bolt.rb +26 -0
- data/examples/shell/resources/splitsentence.py +9 -0
- data/examples/shell/resources/storm.py +206 -0
- data/examples/shell/shell_topology.rb +41 -0
- data/examples/simple/exclamation_bolt.rb +10 -0
- data/examples/simple/exclamation_topology.rb +45 -0
- data/examples/simple/exclamation_topology2.rb +45 -0
- data/examples/simple/kafka_topology.rb +55 -0
- data/examples/simple/random_sentence_spout.rb +21 -0
- data/examples/simple/redis_word_count_topology.rb +61 -0
- data/examples/simple/ruby_version_topology.rb +32 -0
- data/examples/simple/split_sentence_bolt.rb +33 -0
- data/examples/simple/word_count_bolt.rb +19 -0
- data/examples/simple/word_count_topology.rb +38 -0
- data/ivy/settings.xml +11 -0
- data/lib/red_storm.rb +9 -0
- data/lib/red_storm/application.rb +85 -0
- data/lib/red_storm/configuration.rb +16 -0
- data/lib/red_storm/configurator.rb +26 -0
- data/lib/red_storm/environment.rb +41 -0
- data/lib/red_storm/loggable.rb +15 -0
- data/lib/red_storm/proxy/batch_spout.rb +71 -0
- data/lib/red_storm/proxy/bolt.rb +63 -0
- data/lib/red_storm/proxy/proxy_function.rb +48 -0
- data/lib/red_storm/proxy/spout.rb +87 -0
- data/lib/red_storm/simple_bolt.rb +135 -0
- data/lib/red_storm/simple_drpc_topology.rb +87 -0
- data/lib/red_storm/simple_spout.rb +184 -0
- data/lib/red_storm/simple_topology.rb +209 -0
- data/lib/red_storm/topology_launcher.rb +54 -0
- data/lib/red_storm/version.rb +3 -0
- data/lib/tasks/red_storm.rake +272 -0
- data/src/main/redstorm/storm/jruby/JRubyBatchSpout.java +89 -0
- data/src/main/redstorm/storm/jruby/JRubyBolt.java +88 -0
- data/src/main/redstorm/storm/jruby/JRubyProxyFunction.java +59 -0
- data/src/main/redstorm/storm/jruby/JRubyShellBolt.java +26 -0
- data/src/main/redstorm/storm/jruby/JRubyShellSpout.java +26 -0
- data/src/main/redstorm/storm/jruby/JRubySpout.java +107 -0
- metadata +134 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
# see https://github.com/colinsurprenant/redstorm/issues/7
|
4
|
+
module Backtype
|
5
|
+
java_import 'backtype.storm.Config'
|
6
|
+
end
|
7
|
+
|
8
|
+
java_import 'backtype.storm.LocalCluster'
|
9
|
+
java_import 'backtype.storm.LocalDRPC'
|
10
|
+
java_import 'backtype.storm.StormSubmitter'
|
11
|
+
java_import 'backtype.storm.topology.TopologyBuilder'
|
12
|
+
java_import 'backtype.storm.drpc.LinearDRPCTopologyBuilder'
|
13
|
+
java_import 'backtype.storm.tuple.Fields'
|
14
|
+
java_import 'backtype.storm.tuple.Tuple'
|
15
|
+
java_import 'backtype.storm.tuple.Values'
|
16
|
+
|
17
|
+
java_import 'redstorm.storm.jruby.JRubyBolt'
|
18
|
+
java_import 'redstorm.storm.jruby.JRubySpout'
|
19
|
+
java_import 'redstorm.storm.jruby.JRubyBatchSpout'
|
20
|
+
|
21
|
+
java_package 'redstorm'
|
22
|
+
|
23
|
+
# TopologyLauncher is the application entry point when launching a topology. Basically it will
|
24
|
+
# call require on the specified Ruby topology class file path and call its start method
|
25
|
+
class TopologyLauncher
|
26
|
+
|
27
|
+
java_signature 'void main(String[])'
|
28
|
+
def self.main(args)
|
29
|
+
unless args.size > 1
|
30
|
+
puts("Usage: redstorm local|cluster topology_class_file_name")
|
31
|
+
exit(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
env = args[0].to_sym
|
35
|
+
class_path = args[1]
|
36
|
+
|
37
|
+
launch_path = Dir.pwd
|
38
|
+
$:.unshift File.expand_path(launch_path)
|
39
|
+
$:.unshift File.expand_path(launch_path + '/lib')
|
40
|
+
$:.unshift File.expand_path(launch_path + '/target/lib')
|
41
|
+
|
42
|
+
require "#{class_path}"
|
43
|
+
|
44
|
+
topology_name = RedStorm::Configuration.topology_class.respond_to?(:topology_name) ? "/#{RedStorm::Configuration.topology_class.topology_name}" : ''
|
45
|
+
puts("RedStorm v#{RedStorm::VERSION} starting topology #{RedStorm::Configuration.topology_class.name}#{topology_name} in #{env.to_s} environment")
|
46
|
+
RedStorm::Configuration.topology_class.new.start(class_path, env)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def self.camel_case(s)
|
52
|
+
s.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
begin
|
2
|
+
require 'ant'
|
3
|
+
rescue
|
4
|
+
puts("error: unable to load Ant, make sure Ant is installed, in your PATH")
|
5
|
+
puts(" and $ANT_HOME is defined properly.")
|
6
|
+
puts("\nerror detail:\n#{$!}")
|
7
|
+
exit(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'jruby/jrubyc'
|
11
|
+
require 'red_storm'
|
12
|
+
require 'red_storm/application'
|
13
|
+
|
14
|
+
DEP_STORM_VERSION = "0.8.2"
|
15
|
+
DEP_JRUBY_VERSION = "1.6.8"
|
16
|
+
INSTALL_IVY_VERSION = "2.2.0"
|
17
|
+
|
18
|
+
DEFAULT_DEPENDENCIES = {
|
19
|
+
:storm_artifacts => [
|
20
|
+
"storm:storm:#{DEP_STORM_VERSION}, transitive=true",
|
21
|
+
],
|
22
|
+
:topology_artifacts => [
|
23
|
+
"org.jruby:jruby-complete:#{DEP_JRUBY_VERSION}, transitive=false",
|
24
|
+
],
|
25
|
+
}
|
26
|
+
|
27
|
+
task :launch, :env, :ruby_mode, :class_file do |t, args|
|
28
|
+
# use ruby mode parameter or default to current interpreter version
|
29
|
+
version_token = RedStorm.jruby_mode_token(args[:ruby_mode])
|
30
|
+
|
31
|
+
command = case args[:env]
|
32
|
+
when "local"
|
33
|
+
RedStorm::Application.local_storm_command(args[:class_file], args[:ruby_mode])
|
34
|
+
when "cluster"
|
35
|
+
unless File.exist?(TARGET_CLUSTER_JAR)
|
36
|
+
puts("error: cluster jar file #{TARGET_CLUSTER_JAR} not found. Generate it usingw $redstorm jar DIR1 [DIR2, ...]")
|
37
|
+
exit(1)
|
38
|
+
end
|
39
|
+
RedStorm::Application.cluster_storm_command(args[:class_file], args[:ruby_mode])
|
40
|
+
end
|
41
|
+
|
42
|
+
puts("launching #{command}")
|
43
|
+
unless system(command)
|
44
|
+
puts($!)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
task :clean do
|
49
|
+
ant.delete :dir => TARGET_DIR
|
50
|
+
end
|
51
|
+
|
52
|
+
task :clean_jar do
|
53
|
+
ant.delete :file => TARGET_CLUSTER_JAR
|
54
|
+
end
|
55
|
+
|
56
|
+
task :setup do
|
57
|
+
puts("\n--> Setting up target directories")
|
58
|
+
ant.mkdir :dir => TARGET_DIR
|
59
|
+
ant.mkdir :dir => TARGET_CLASSES_DIR
|
60
|
+
ant.mkdir :dir => TARGET_DEPENDENCY_DIR
|
61
|
+
ant.mkdir :dir => TARGET_SRC_DIR
|
62
|
+
ant.mkdir :dir => TARGET_GEM_DIR
|
63
|
+
ant.mkdir :dir => TARGET_SPECS_DIR
|
64
|
+
ant.path :id => 'classpath' do
|
65
|
+
fileset :dir => TARGET_DEPENDENCY_DIR
|
66
|
+
fileset :dir => TARGET_CLASSES_DIR
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
task :install => [:deps, :build] do
|
71
|
+
puts("\nRedStorm install completed. All dependencies installed in #{TARGET_DIR}")
|
72
|
+
end
|
73
|
+
|
74
|
+
task :examples do
|
75
|
+
if File.identical?(SRC_EXAMPLES, DST_EXAMPLES)
|
76
|
+
STDERR.puts("error: cannot copy examples into itself")
|
77
|
+
exit(1)
|
78
|
+
end
|
79
|
+
if File.exist?(DST_EXAMPLES)
|
80
|
+
STDERR.puts("error: directory #{DST_EXAMPLES} already exists")
|
81
|
+
exit(1)
|
82
|
+
end
|
83
|
+
|
84
|
+
puts("\n--> Installing examples into #{DST_EXAMPLES}")
|
85
|
+
FileUtils.mkdir(DST_EXAMPLES)
|
86
|
+
FileUtils.cp_r(Dir["#{SRC_EXAMPLES}/*"], DST_EXAMPLES)
|
87
|
+
end
|
88
|
+
|
89
|
+
task :copy_red_storm do
|
90
|
+
FileUtils.cp_r(REDSTORM_LIB_DIR, TARGET_DIR)
|
91
|
+
end
|
92
|
+
|
93
|
+
task :build => [:setup, :copy_red_storm] do
|
94
|
+
# compile the JRuby proxy classes to Java
|
95
|
+
build_jruby("#{REDSTORM_LIB_DIR}/red_storm/proxy")
|
96
|
+
|
97
|
+
# compile the generated Java proxy classes
|
98
|
+
build_java_dir("#{TARGET_SRC_DIR}")
|
99
|
+
|
100
|
+
# generate the JRuby topology launcher
|
101
|
+
build_jruby("#{REDSTORM_LIB_DIR}/red_storm/topology_launcher.rb")
|
102
|
+
|
103
|
+
# compile the JRuby proxy classes
|
104
|
+
build_java_dir("#{REDSTORM_JAVA_SRC_DIR}")
|
105
|
+
|
106
|
+
# compile the JRuby proxy classes
|
107
|
+
build_java_dir("#{TARGET_SRC_DIR}")
|
108
|
+
end
|
109
|
+
|
110
|
+
task :bundle, [:groups] => :setup do |t, args|
|
111
|
+
require 'bundler'
|
112
|
+
defaulted_args = {:groups => 'default'}.merge(args.to_hash.delete_if{|k, v| v.to_s.empty?})
|
113
|
+
groups = defaulted_args[:groups].split(':').map(&:to_sym)
|
114
|
+
Bundler.definition.specs_for(groups).each do |spec|
|
115
|
+
unless spec.full_name =~ /^bundler-\d+/
|
116
|
+
destination_path = "#{TARGET_GEM_DIR}/#{spec.full_name}"
|
117
|
+
unless File.directory?(destination_path)
|
118
|
+
puts("installing gem #{spec.full_name} into #{destination_path}")
|
119
|
+
# copy the actual gem dir
|
120
|
+
FileUtils.cp_r(spec.full_gem_path, destination_path)
|
121
|
+
# copy the gemspec into the specifications/ dir
|
122
|
+
FileUtils.cp_r(spec.loaded_from, TARGET_SPECS_DIR)
|
123
|
+
# strip the .git directory from git dependencies, it can be huge
|
124
|
+
FileUtils.rm_rf("#{destination_path}/.git")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
namespace :ivy do
|
131
|
+
task :download do
|
132
|
+
mkdir_p DST_IVY_DIR
|
133
|
+
ant.get({
|
134
|
+
:src => "http://repo1.maven.org/maven2/org/apache/ivy/ivy/#{INSTALL_IVY_VERSION}/ivy-#{INSTALL_IVY_VERSION}.jar",
|
135
|
+
:dest => "#{DST_IVY_DIR}/ivy-#{INSTALL_IVY_VERSION}.jar",
|
136
|
+
:usetimestamp => true,
|
137
|
+
})
|
138
|
+
end
|
139
|
+
|
140
|
+
task :install => :download do
|
141
|
+
ant.path :id => 'ivy.lib.path' do
|
142
|
+
fileset :dir => DST_IVY_DIR, :includes => '*.jar'
|
143
|
+
end
|
144
|
+
|
145
|
+
ant.taskdef({
|
146
|
+
:resource => "org/apache/ivy/ant/antlib.xml",
|
147
|
+
:classpathref => "ivy.lib.path",
|
148
|
+
#:uri => "antlib:org.apache.ivy.ant",
|
149
|
+
})
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
task :deps => "ivy:install" do
|
154
|
+
puts("\n--> Installing dependencies")
|
155
|
+
|
156
|
+
dependencies = File.exists?(CUSTOM_DEPENDENCIES) ? eval(File.read(CUSTOM_DEPENDENCIES)) : DEFAULT_DEPENDENCIES
|
157
|
+
ant.configure :file => File.exists?(CUSTOM_IVY_SETTINGS) ? CUSTOM_IVY_SETTINGS : DEFAULT_IVY_SETTINGS
|
158
|
+
|
159
|
+
dependencies[:storm_artifacts].each do |dependency|
|
160
|
+
artifact, transitive = dependency.split(/\s*,\s*/)
|
161
|
+
ivy_retrieve(*artifact.split(':').concat([transitive.split(/\s*=\s*/).last, "#{TARGET_DEPENDENCY_DIR}/storm", "default"]))
|
162
|
+
end
|
163
|
+
|
164
|
+
dependencies[:topology_artifacts].each do |dependency|
|
165
|
+
artifact, transitive = dependency.split(/\s*,\s*/)
|
166
|
+
ivy_retrieve(*artifact.split(':').concat([transitive.split(/\s*=\s*/).last, "#{TARGET_DEPENDENCY_DIR}/topology", "default"]))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
task :jar, [:include_dir] => [:clean_jar] do |t, args|
|
171
|
+
puts("\n--> Generating JAR file #{TARGET_CLUSTER_JAR}")
|
172
|
+
|
173
|
+
ant.jar :destfile => TARGET_CLUSTER_JAR do
|
174
|
+
# rejar all topology jars
|
175
|
+
Dir["target/dependency/topology/default/*.jar"].each do |jar|
|
176
|
+
puts("Extracting #{jar}")
|
177
|
+
zipfileset :src => jar, :includes => "**/*"
|
178
|
+
end
|
179
|
+
fileset :dir => TARGET_DIR do
|
180
|
+
include :name => "gems/**"
|
181
|
+
end
|
182
|
+
fileset :dir => TARGET_CLASSES_DIR
|
183
|
+
# red_storm.rb and red_storm/* must be in root of jar so that "require 'red_storm'"
|
184
|
+
# in bolts/spouts works in jar context
|
185
|
+
fileset :dir => TARGET_LIB_DIR do
|
186
|
+
exclude :name => "tasks/**"
|
187
|
+
end
|
188
|
+
if args[:include_dir]
|
189
|
+
dirs = args[:include_dir].split(":")
|
190
|
+
|
191
|
+
# first add any resources/ dir in the tree in the jar root - requirement for ShellBolt multilang resources
|
192
|
+
dirs.each do |dir|
|
193
|
+
resources_dirs = Dir.glob("#{dir}/**/resources")
|
194
|
+
resources_dirs.each do |resources_dir|
|
195
|
+
resources_parent = resources_dir.gsub("/resources", "")
|
196
|
+
fileset :dir => resources_parent do
|
197
|
+
include :name => "resources/**/*"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# include complete source dir tree (note we don't care about potential duplicated resources dir)
|
203
|
+
fileset :dir => CWD do
|
204
|
+
dirs.each{|dir| include :name => "#{dir}/**/*"}
|
205
|
+
end
|
206
|
+
end
|
207
|
+
manifest do
|
208
|
+
attribute :name => "Main-Class", :value => "redstorm.TopologyLauncher"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
puts("\nRedStorm generated JAR file #{TARGET_CLUSTER_JAR}")
|
212
|
+
end
|
213
|
+
|
214
|
+
def build_java_dir(source_folder)
|
215
|
+
puts("\n--> Compiling Java")
|
216
|
+
ant.javac(
|
217
|
+
:srcdir => source_folder,
|
218
|
+
:destdir => TARGET_CLASSES_DIR,
|
219
|
+
:classpathref => 'classpath',
|
220
|
+
:source => "1.6",
|
221
|
+
:target => "1.6",
|
222
|
+
:debug => "yes",
|
223
|
+
:includeantruntime => "no",
|
224
|
+
:verbose => false,
|
225
|
+
:listfiles => true
|
226
|
+
) do
|
227
|
+
# compilerarg :value => "-Xlint:unchecked"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def build_jruby(source_path)
|
232
|
+
puts("\n--> Compiling JRuby")
|
233
|
+
Dir.chdir(RedStorm::REDSTORM_HOME) do
|
234
|
+
argv = []
|
235
|
+
argv << '-t' << TARGET_SRC_DIR
|
236
|
+
argv << '--verbose'
|
237
|
+
argv << '--java'
|
238
|
+
Dir["#{TARGET_DEPENDENCY_DIR}/storm/default/*.jar"].each do |jar|
|
239
|
+
argv << '-c' << %("#{jar}")
|
240
|
+
end
|
241
|
+
argv << '-c' << %("#{TARGET_CLASSES_DIR}")
|
242
|
+
argv << source_path
|
243
|
+
status = JRuby::Compiler::compile_argv(argv)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def truefalse(s)
|
248
|
+
return true if s.to_s.downcase =~ /1|yes|true/
|
249
|
+
return false if s.to_s.downcase =~ /0|no|false/
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
|
253
|
+
def ivy_retrieve(org, mod, rev, transitive, dir, conf)
|
254
|
+
ant.resolve({
|
255
|
+
:organisation => org,
|
256
|
+
:module => mod,
|
257
|
+
:revision => rev,
|
258
|
+
:inline => true,
|
259
|
+
:transitive => truefalse(transitive),
|
260
|
+
:conf => conf,
|
261
|
+
})
|
262
|
+
|
263
|
+
ant.retrieve({
|
264
|
+
:organisation => org,
|
265
|
+
:module => mod,
|
266
|
+
:revision => rev,
|
267
|
+
:pattern => "#{dir}/[conf]/[artifact]-[revision].[ext]",
|
268
|
+
:inline => true,
|
269
|
+
:transitive => truefalse(transitive),
|
270
|
+
:conf => conf,
|
271
|
+
})
|
272
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
package redstorm.storm.jruby;
|
2
|
+
|
3
|
+
import storm.trident.operation.TridentCollector;
|
4
|
+
import backtype.storm.task.TopologyContext;
|
5
|
+
import storm.trident.spout.IBatchSpout;
|
6
|
+
import backtype.storm.topology.OutputFieldsDeclarer;
|
7
|
+
import backtype.storm.tuple.Tuple;
|
8
|
+
import backtype.storm.tuple.Fields;
|
9
|
+
import java.util.Map;
|
10
|
+
|
11
|
+
/**
|
12
|
+
* the JRubyBatchSpout class is a simple proxy class to the actual spout implementation in JRuby.
|
13
|
+
* this proxy is required to bypass the serialization/deserialization process when dispatching
|
14
|
+
* the spout to the workers. JRuby does not yet support serialization from Java
|
15
|
+
* (Java serialization call on a JRuby class).
|
16
|
+
*
|
17
|
+
* Note that the JRuby spout proxy class is instanciated in the open method which is called after
|
18
|
+
* deserialization at the worker and in both the declareOutputFields and isDistributed methods which
|
19
|
+
* are called once before serialization at topology creation.
|
20
|
+
*/
|
21
|
+
public class JRubyBatchSpout implements IBatchSpout {
|
22
|
+
IBatchSpout _proxySpout;
|
23
|
+
String _realSpoutClassName;
|
24
|
+
String _baseClassPath;
|
25
|
+
String[] _fields;
|
26
|
+
|
27
|
+
/**
|
28
|
+
* create a new JRubyBatchSpout
|
29
|
+
*
|
30
|
+
* @param baseClassPath the topology/project base JRuby class file path
|
31
|
+
* @param realSpoutClassName the fully qualified JRuby spout implementation class name
|
32
|
+
*/
|
33
|
+
public JRubyBatchSpout(String baseClassPath, String realSpoutClassName, String[] fields) {
|
34
|
+
_baseClassPath = baseClassPath;
|
35
|
+
_realSpoutClassName = realSpoutClassName;
|
36
|
+
_fields = fields;
|
37
|
+
}
|
38
|
+
|
39
|
+
@Override
|
40
|
+
public void open(final Map conf, final TopologyContext context) {
|
41
|
+
// create instance of the jruby proxy class here, after deserialization in the workers.
|
42
|
+
_proxySpout = newProxySpout(_baseClassPath, _realSpoutClassName);
|
43
|
+
_proxySpout.open(conf, context);
|
44
|
+
}
|
45
|
+
|
46
|
+
@Override
|
47
|
+
public void emitBatch(final long batchId, final TridentCollector collector) {
|
48
|
+
_proxySpout.emitBatch(batchId, collector);
|
49
|
+
}
|
50
|
+
|
51
|
+
|
52
|
+
@Override
|
53
|
+
public void close() {
|
54
|
+
_proxySpout.close();
|
55
|
+
}
|
56
|
+
|
57
|
+
@Override
|
58
|
+
public void ack(final long batchId) {
|
59
|
+
_proxySpout.ack(batchId);
|
60
|
+
}
|
61
|
+
|
62
|
+
@Override
|
63
|
+
public Fields getOutputFields() {
|
64
|
+
// getOutputFields is executed in the topology creation time before serialisation.
|
65
|
+
// do not set the _proxySpout instance variable here to avoid JRuby serialization
|
66
|
+
// issues. Just create tmp spout instance to call declareOutputFields.
|
67
|
+
IBatchSpout spout = newProxySpout(_baseClassPath, _realSpoutClassName);
|
68
|
+
return spout.getOutputFields();
|
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 _proxySpout instance variable here to avoid JRuby serialization
|
75
|
+
// issues. Just create tmp spout instance to call declareOutputFields.
|
76
|
+
IBatchSpout spout = newProxySpout(_baseClassPath, _realSpoutClassName);
|
77
|
+
return spout.getComponentConfiguration();
|
78
|
+
}
|
79
|
+
|
80
|
+
private static IBatchSpout newProxySpout(String baseClassPath, String realSpoutClassName) {
|
81
|
+
try {
|
82
|
+
redstorm.proxy.BatchSpout proxy = new redstorm.proxy.BatchSpout(baseClassPath, realSpoutClassName);
|
83
|
+
return proxy;
|
84
|
+
}
|
85
|
+
catch (Exception e) {
|
86
|
+
throw new RuntimeException(e);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
package redstorm.storm.jruby;
|
2
|
+
|
3
|
+
import backtype.storm.task.OutputCollector;
|
4
|
+
import backtype.storm.task.TopologyContext;
|
5
|
+
import backtype.storm.topology.IRichBolt;
|
6
|
+
import backtype.storm.topology.OutputFieldsDeclarer;
|
7
|
+
import backtype.storm.tuple.Tuple;
|
8
|
+
import backtype.storm.tuple.Fields;
|
9
|
+
import java.util.Map;
|
10
|
+
|
11
|
+
/**
|
12
|
+
* the JRubyBolt class is a simple proxy class to the actual bolt implementation in JRuby.
|
13
|
+
* this proxy is required to bypass the serialization/deserialization process when dispatching
|
14
|
+
* the bolts to the workers. JRuby does not yet support serialization from Java
|
15
|
+
* (Java serialization call on a JRuby class).
|
16
|
+
*
|
17
|
+
* Note that the JRuby bolt proxy class is instanciated in the prepare method which is called after
|
18
|
+
* deserialization at the worker and in the declareOutputFields method which is called once before
|
19
|
+
* serialization at topology creation.
|
20
|
+
*/
|
21
|
+
public class JRubyBolt implements IRichBolt {
|
22
|
+
IRichBolt _proxyBolt;
|
23
|
+
String _realBoltClassName;
|
24
|
+
String _baseClassPath;
|
25
|
+
String[] _fields;
|
26
|
+
|
27
|
+
/**
|
28
|
+
* create a new JRubyBolt
|
29
|
+
*
|
30
|
+
* @param baseClassPath the topology/project base JRuby class file path
|
31
|
+
* @param realBoltClassName the fully qualified JRuby bolt implementation class name
|
32
|
+
*/
|
33
|
+
public JRubyBolt(String baseClassPath, String realBoltClassName, String[] fields) {
|
34
|
+
_baseClassPath = baseClassPath;
|
35
|
+
_realBoltClassName = realBoltClassName;
|
36
|
+
_fields = fields;
|
37
|
+
}
|
38
|
+
|
39
|
+
@Override
|
40
|
+
public void prepare(final Map stormConf, final TopologyContext context, final OutputCollector collector) {
|
41
|
+
// create instance of the jruby class here, after deserialization in the workers.
|
42
|
+
_proxyBolt = newProxyBolt(_baseClassPath, _realBoltClassName);
|
43
|
+
_proxyBolt.prepare(stormConf, context, collector);
|
44
|
+
}
|
45
|
+
|
46
|
+
@Override
|
47
|
+
public void execute(Tuple input) {
|
48
|
+
_proxyBolt.execute(input);
|
49
|
+
}
|
50
|
+
|
51
|
+
@Override
|
52
|
+
public void cleanup() {
|
53
|
+
_proxyBolt.cleanup();
|
54
|
+
}
|
55
|
+
|
56
|
+
@Override
|
57
|
+
public void declareOutputFields(OutputFieldsDeclarer declarer) {
|
58
|
+
// declareOutputFields is executed in the topology creation time, before serialisation.
|
59
|
+
// do not set the _proxyBolt instance variable here to avoid JRuby serialization
|
60
|
+
// issues. Just create tmp bolt instance to call declareOutputFields.
|
61
|
+
if (_fields.length > 0) {
|
62
|
+
declarer.declare(new Fields(_fields));
|
63
|
+
} else {
|
64
|
+
IRichBolt bolt = newProxyBolt(_baseClassPath, _realBoltClassName);
|
65
|
+
bolt.declareOutputFields(declarer);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
@Override
|
70
|
+
public Map<String, Object> getComponentConfiguration() {
|
71
|
+
// getComponentConfiguration is executed in the topology creation time, before serialisation.
|
72
|
+
// do not set the _proxyBolt instance variable here to avoid JRuby serialization
|
73
|
+
// issues. Just create tmp bolt instance to call declareOutputFields.
|
74
|
+
IRichBolt bolt = newProxyBolt(_baseClassPath, _realBoltClassName);
|
75
|
+
return bolt.getComponentConfiguration();
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
private static IRichBolt newProxyBolt(String baseClassPath, String realBoltClassName) {
|
80
|
+
try {
|
81
|
+
redstorm.proxy.Bolt proxy = new redstorm.proxy.Bolt(baseClassPath, realBoltClassName);
|
82
|
+
return proxy;
|
83
|
+
}
|
84
|
+
catch (Exception e) {
|
85
|
+
throw new RuntimeException(e);
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|