kb-redstorm 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|