redstorm 0.6.2 → 0.6.3

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 CHANGED
@@ -56,4 +56,10 @@
56
56
  # 0.6.2, 07-10-2012
57
57
  - issue #39, spout on_receive block will not be evaluated if :emit => false
58
58
  - issue #40, bolt fail method missing
59
- - integration tests support
59
+ - integration tests support
60
+
61
+ # 0.6.3, 10-09-2012
62
+ - issue #28, allow specification of output_fields in topology DSL
63
+ - issue #41, add support for ShellBolt and ShellSpout
64
+ - issue #49, redstorm bundle not picking up default group in Gemfile
65
+ - support constructor parameters for Java spout/bolt in topology DSL
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RedStorm v0.6.2 - JRuby on Storm
1
+ # RedStorm v0.6.3 - JRuby on Storm
2
2
 
3
3
  [![build status](https://secure.travis-ci.org/colinsurprenant/redstorm.png)](http://travis-ci.org/colinsurprenant/redstorm)
4
4
 
@@ -58,7 +58,7 @@ $ redstorm local|cluster [--1.8|--1.9] ...
58
58
 
59
59
  ``` ruby
60
60
  source :rubygems
61
- gem "redstorm", "~> 0.6.2"
61
+ gem "redstorm", "~> 0.6.3"
62
62
  ```
63
63
 
64
64
  ## Usage overview
@@ -255,6 +255,25 @@ The [Storm wiki](https://github.com/nathanmarz/storm/wiki) has instructions on [
255
255
 
256
256
  [Ruby DSL Documentation](https://github.com/colinsurprenant/redstorm/wiki/Ruby-DSL-Documentation)
257
257
 
258
+ ## Multilang ShellSpout & ShellBolt support
259
+
260
+ Please refer to [Using non JVM languages with Storm](https://github.com/nathanmarz/storm/wiki/Using-non-JVM-languages-with-Storm) for the complete information on Multilang & shelling in Storm.
261
+
262
+ In RedStorm *ShellSpout* and *ShellBolt* are supported using the following construct in the topology definition:
263
+
264
+ ``` ruby
265
+ bolt JRubyShellBolt, ["python", "splitsentence.py"] do
266
+ output_fields "word"
267
+ source SimpleSpout, :shuffle
268
+ end
269
+ ```
270
+
271
+ - `JRubyShellBolt` must be used for a *ShellBolt* and the array argument `["python", "splitsentence.py"]` are the arguments to the class constructor and are the *commands* to the *ShellBolt*.
272
+
273
+ - The directory containing the topology class **must** contain a `resources/` directory with all the shell files.
274
+
275
+ See the [shell topology example](https://github.com/colinsurprenant/redstorm/tree/master/examples/shell)
276
+
258
277
  ## RedStorm Development
259
278
 
260
279
  It is possible to fork the RedStorm project and run local and remote/cluster topologies directly from the project sources without installing the gem. This is a useful setup when contributing to the project.
@@ -307,10 +326,10 @@ Some ways you can contribute:
307
326
 
308
327
  If you want to list your RedStorm project here, contact me.
309
328
 
310
- - [Tweigeist](https://github.com/colinsurprenant/tweitgeist) - realtime computation of the top trending hashtags on Twitter. [Live Demo](http://tweitgeist.needium.com).
329
+ - [Tweigeist](https://github.com/colinsurprenant/tweitgeist) - realtime computation of the top trending hashtags on Twitter. Live Demo in search of a new home.
311
330
 
312
331
  ## Author
313
- Colin Surprenant, [@colinsurprenant][twitter], [http://github.com/colinsurprenant][github], colin.surprenant@gmail.com, colin.surprenant@needium.com
332
+ Colin Surprenant, [@colinsurprenant][twitter], [http://github.com/colinsurprenant][github], colin.surprenant@gmail.com
314
333
 
315
334
  ## Contributors
316
335
  Theo Hultberg, https://github.com/iconara
@@ -0,0 +1,9 @@
1
+ import storm
2
+
3
+ class SplitSentenceBolt(storm.BasicBolt):
4
+ def process(self, tup):
5
+ words = tup.values[0].split(" ")
6
+ for word in words:
7
+ storm.emit([word])
8
+
9
+ SplitSentenceBolt().run()
@@ -0,0 +1,206 @@
1
+ import sys
2
+ import os
3
+ import traceback
4
+ from collections import deque
5
+
6
+ try:
7
+ import simplejson as json
8
+ except ImportError:
9
+ import json
10
+
11
+ json_encode = lambda x: json.dumps(x)
12
+ json_decode = lambda x: json.loads(x)
13
+
14
+ #reads lines and reconstructs newlines appropriately
15
+ def readMsg():
16
+ msg = ""
17
+ while True:
18
+ line = sys.stdin.readline()[0:-1]
19
+ if line == "end":
20
+ break
21
+ msg = msg + line + "\n"
22
+ return json_decode(msg[0:-1])
23
+
24
+ MODE = None
25
+ ANCHOR_TUPLE = None
26
+
27
+ #queue up commands we read while trying to read taskids
28
+ pending_commands = deque()
29
+
30
+ def readTaskIds():
31
+ if pending_taskids:
32
+ return pending_taskids.popleft()
33
+ else:
34
+ msg = readMsg()
35
+ while type(msg) is not list:
36
+ pending_commands.append(msg)
37
+ msg = readMsg()
38
+ return msg
39
+
40
+ #queue up taskids we read while trying to read commands/tuples
41
+ pending_taskids = deque()
42
+
43
+ def readCommand():
44
+ if pending_commands:
45
+ return pending_commands.popleft()
46
+ else:
47
+ msg = readMsg()
48
+ while type(msg) is list:
49
+ pending_taskids.append(msg)
50
+ msg = readMsg()
51
+ return msg
52
+
53
+ def readTuple():
54
+ cmd = readCommand()
55
+ return Tuple(cmd["id"], cmd["comp"], cmd["stream"], cmd["task"], cmd["tuple"])
56
+
57
+ def sendMsgToParent(msg):
58
+ print json_encode(msg)
59
+ print "end"
60
+ sys.stdout.flush()
61
+
62
+ def sync():
63
+ sendMsgToParent({'command':'sync'})
64
+
65
+ def sendpid(heartbeatdir):
66
+ pid = os.getpid()
67
+ sendMsgToParent({'pid':pid})
68
+ open(heartbeatdir + "/" + str(pid), "w").close()
69
+
70
+ def emit(*args, **kwargs):
71
+ __emit(*args, **kwargs)
72
+ return readTaskIds()
73
+
74
+ def emitDirect(task, *args, **kwargs):
75
+ kwargs[directTask] = task
76
+ __emit(*args, **kwargs)
77
+
78
+ def __emit(*args, **kwargs):
79
+ global MODE
80
+ if MODE == Bolt:
81
+ emitBolt(*args, **kwargs)
82
+ elif MODE == Spout:
83
+ emitSpout(*args, **kwargs)
84
+
85
+ def emitBolt(tup, stream=None, anchors = [], directTask=None):
86
+ global ANCHOR_TUPLE
87
+ if ANCHOR_TUPLE is not None:
88
+ anchors = [ANCHOR_TUPLE]
89
+ m = {"command": "emit"}
90
+ if stream is not None:
91
+ m["stream"] = stream
92
+ m["anchors"] = map(lambda a: a.id, anchors)
93
+ if directTask is not None:
94
+ m["task"] = directTask
95
+ m["tuple"] = tup
96
+ sendMsgToParent(m)
97
+
98
+ def emitSpout(tup, stream=None, id=None, directTask=None):
99
+ m = {"command": "emit"}
100
+ if id is not None:
101
+ m["id"] = id
102
+ if stream is not None:
103
+ m["stream"] = stream
104
+ if directTask is not None:
105
+ m["task"] = directTask
106
+ m["tuple"] = tup
107
+ sendMsgToParent(m)
108
+
109
+ def ack(tup):
110
+ sendMsgToParent({"command": "ack", "id": tup.id})
111
+
112
+ def fail(tup):
113
+ sendMsgToParent({"command": "fail", "id": tup.id})
114
+
115
+ def log(msg):
116
+ sendMsgToParent({"command": "log", "msg": msg})
117
+
118
+ def initComponent():
119
+ setupInfo = readMsg()
120
+ sendpid(setupInfo['pidDir'])
121
+ return [setupInfo['conf'], setupInfo['context']]
122
+
123
+ class Tuple:
124
+ def __init__(self, id, component, stream, task, values):
125
+ self.id = id
126
+ self.component = component
127
+ self.stream = stream
128
+ self.task = task
129
+ self.values = values
130
+
131
+ def __repr__(self):
132
+ return '<%s%s>' % (
133
+ self.__class__.__name__,
134
+ ''.join(' %s=%r' % (k, self.__dict__[k]) for k in sorted(self.__dict__.keys())))
135
+
136
+ class Bolt:
137
+ def initialize(self, stormconf, context):
138
+ pass
139
+
140
+ def process(self, tuple):
141
+ pass
142
+
143
+ def run(self):
144
+ global MODE
145
+ MODE = Bolt
146
+ conf, context = initComponent()
147
+ self.initialize(conf, context)
148
+ try:
149
+ while True:
150
+ tup = readTuple()
151
+ self.process(tup)
152
+ except Exception, e:
153
+ log(traceback.format_exc(e))
154
+
155
+ class BasicBolt:
156
+ def initialize(self, stormconf, context):
157
+ pass
158
+
159
+ def process(self, tuple):
160
+ pass
161
+
162
+ def run(self):
163
+ global MODE
164
+ MODE = Bolt
165
+ global ANCHOR_TUPLE
166
+ conf, context = initComponent()
167
+ self.initialize(conf, context)
168
+ try:
169
+ while True:
170
+ tup = readTuple()
171
+ ANCHOR_TUPLE = tup
172
+ self.process(tup)
173
+ ack(tup)
174
+ except Exception, e:
175
+ log(traceback.format_exc(e))
176
+
177
+ class Spout:
178
+ def initialize(self, conf, context):
179
+ pass
180
+
181
+ def ack(self, id):
182
+ pass
183
+
184
+ def fail(self, id):
185
+ pass
186
+
187
+ def nextTuple(self):
188
+ pass
189
+
190
+ def run(self):
191
+ global MODE
192
+ MODE = Spout
193
+ conf, context = initComponent()
194
+ self.initialize(conf, context)
195
+ try:
196
+ while True:
197
+ msg = readCommand()
198
+ if msg["command"] == "next":
199
+ self.nextTuple()
200
+ if msg["command"] == "ack":
201
+ self.ack(msg["id"])
202
+ if msg["command"] == "fail":
203
+ self.fail(msg["id"])
204
+ sync()
205
+ except Exception, e:
206
+ log(traceback.format_exc(e))
@@ -0,0 +1,41 @@
1
+ require 'red_storm'
2
+ require 'thread'
3
+
4
+ java_import 'redstorm.storm.jruby.JRubyShellBolt'
5
+
6
+ class SimpleSpout < RedStorm::SimpleSpout
7
+ on_init do
8
+ @q = Queue.new
9
+ @q << "the quick red fox"
10
+ end
11
+
12
+ on_send do
13
+ # avoid putting the thread to sleep endlessly on @q.pop which will prevent local cluster.shutdown
14
+ sleep(1)
15
+ @q.pop unless @q.empty?
16
+ end
17
+ end
18
+
19
+ class ShellTopology < RedStorm::SimpleTopology
20
+ spout SimpleSpout do
21
+ output_fields :string
22
+ end
23
+
24
+ bolt JRubyShellBolt, ["python", "splitsentence.py"] do
25
+ output_fields "word"
26
+ source SimpleSpout, :shuffle
27
+ end
28
+
29
+ configure do |env|
30
+ debug true
31
+ end
32
+
33
+ on_submit do |env|
34
+ case env
35
+ when :local
36
+ sleep(10)
37
+ cluster.shutdown
38
+ end
39
+ end
40
+ end
41
+
@@ -25,7 +25,8 @@ module RedStorm
25
25
  TASKS_FILE = "#{RedStorm::REDSTORM_HOME}/lib/tasks/red_storm.rake"
26
26
 
27
27
  def self.local_storm_command(class_file, ruby_mode = nil)
28
- "java -Djruby.compat.version=#{RedStorm.jruby_mode_token(ruby_mode)} -cp \"#{TARGET_CLASSES_DIR}:#{TARGET_DEPENDENCY_DIR}/*\" redstorm.TopologyLauncher local #{class_file}"
28
+ src_dir = File.expand_path(File.dirname(class_file))
29
+ "java -Djruby.compat.version=#{RedStorm.jruby_mode_token(ruby_mode)} -cp \"#{TARGET_CLASSES_DIR}:#{TARGET_DEPENDENCY_DIR}/*:#{src_dir}/\" redstorm.TopologyLauncher local #{class_file}"
29
30
  end
30
31
 
31
32
  def self.cluster_storm_command(class_file, ruby_mode = nil)
@@ -67,6 +68,12 @@ module RedStorm
67
68
  end
68
69
  usage
69
70
  end
71
+
72
+ def self.subshell(command)
73
+ out = IO.popen([command, {:err => [:child, :out]}]) {|io| io.read}
74
+ [!!$?.success?, out]
75
+ end
76
+
70
77
  end
71
78
 
72
79
  end
@@ -2,6 +2,7 @@ require 'java'
2
2
  require 'red_storm/configuration'
3
3
  require 'red_storm/configurator'
4
4
 
5
+
5
6
  module RedStorm
6
7
 
7
8
  class TopologyDefinitionError < StandardError; end
@@ -13,14 +14,20 @@ module RedStorm
13
14
  DEFAULT_BOLT_PARALLELISM = 1
14
15
 
15
16
  class ComponentDefinition < Configurator
16
- attr_reader :clazz, :parallelism
17
+ attr_reader :clazz, :constructor_args, :parallelism
17
18
  attr_accessor :id # ids are forced to string
18
19
 
19
- def initialize(component_class, id, parallelism)
20
+ def initialize(component_class, constructor_args, id, parallelism)
20
21
  super()
21
22
  @clazz = component_class
23
+ @constructor_args = constructor_args
22
24
  @id = id.to_s
23
25
  @parallelism = parallelism
26
+ @output_fields = []
27
+ end
28
+
29
+ def output_fields(*args)
30
+ args.empty? ? @output_fields : @output_fields = args.map(&:to_s)
24
31
  end
25
32
 
26
33
  def is_java?
@@ -29,13 +36,22 @@ module RedStorm
29
36
  end
30
37
 
31
38
  class SpoutDefinition < ComponentDefinition
39
+
40
+ # WARNING non-dry see BoltDefinition#new_instance
32
41
  def new_instance(base_class_path)
33
- is_java? ? @clazz.new : JRubySpout.new(base_class_path, @clazz.name)
42
+ if @clazz.name == "Java::RedstormStormJruby::JRubyShellSpout"
43
+ @clazz.new(constructor_args, @output_fields)
44
+ elsif is_java?
45
+ @clazz.new(*constructor_args)
46
+ else
47
+ JRubySpout.new(base_class_path, @clazz.name, @output_fields)
48
+ end
49
+ # is_java? ? @clazz.new : JRubySpout.new(base_class_path, @clazz.name)
34
50
  end
35
51
  end
36
52
 
37
53
  class BoltDefinition < ComponentDefinition
38
- attr_accessor :sources
54
+ attr_accessor :sources, :command
39
55
 
40
56
  def initialize(*args)
41
57
  super
@@ -72,7 +88,15 @@ module RedStorm
72
88
  end
73
89
 
74
90
  def new_instance(base_class_path)
75
- is_java? ? @clazz.new : JRubyBolt.new(base_class_path, @clazz.name)
91
+ # WARNING non-dry see BoltDefinition#new_instance
92
+ if @clazz.name == "Java::RedstormStormJruby::JRubyShellBolt"
93
+ @clazz.new(constructor_args, @output_fields)
94
+ elsif is_java?
95
+ @clazz.new(*constructor_args)
96
+ else
97
+ JRubyBolt.new(base_class_path, @clazz.name, @output_fields)
98
+ end
99
+ # is_java? ? @clazz.new : @clazz.is_a?(SimpleBolt) ? JRubyBolt.new(base_class_path, @clazz.name) : @clazz.new
76
100
  end
77
101
  end
78
102
 
@@ -80,16 +104,24 @@ module RedStorm
80
104
  @log ||= org.apache.log4j.Logger.getLogger(self.name)
81
105
  end
82
106
 
83
- def self.spout(spout_class, options = {}, &spout_block)
107
+ # def self.spout(spout_class, contructor_args = [], options = {}, &spout_block)
108
+ def self.spout(spout_class, *args, &spout_block)
109
+ options = args.last.is_a?(Hash) ? args.pop : {}
110
+ contructor_args = !args.empty? ? args.pop : []
84
111
  spout_options = {:id => self.underscore(spout_class), :parallelism => DEFAULT_SPOUT_PARALLELISM}.merge(options)
85
- spout = SpoutDefinition.new(spout_class, spout_options[:id], spout_options[:parallelism])
112
+
113
+ spout = SpoutDefinition.new(spout_class, contructor_args, spout_options[:id], spout_options[:parallelism])
86
114
  spout.instance_exec(&spout_block) if block_given?
87
115
  self.components << spout
88
116
  end
89
117
 
90
- def self.bolt(bolt_class, options = {}, &bolt_block)
118
+ # def self.bolt(bolt_class, contructor_args = [], options = {}, &bolt_block)
119
+ def self.bolt(bolt_class, *args, &bolt_block)
120
+ options = args.last.is_a?(Hash) ? args.pop : {}
121
+ contructor_args = !args.empty? ? args.pop : []
91
122
  bolt_options = {:id => self.underscore(bolt_class), :parallelism => DEFAULT_BOLT_PARALLELISM}.merge(options)
92
- bolt = BoltDefinition.new(bolt_class, bolt_options[:id], bolt_options[:parallelism])
123
+
124
+ bolt = BoltDefinition.new(bolt_class, contructor_args, bolt_options[:id], bolt_options[:parallelism])
93
125
  raise(TopologyDefinitionError, "#{bolt.clazz.name}, #{bolt.id}, bolt definition body required") unless block_given?
94
126
  bolt.instance_exec(&bolt_block)
95
127
  self.components << bolt
@@ -1,3 +1,3 @@
1
1
  module RedStorm
2
- VERSION = '0.6.2'
2
+ VERSION = '0.6.3'
3
3
  end
@@ -88,8 +88,22 @@ task :jar, [:include_dir] => [:unpack, :clean_jar] do |t, args|
88
88
  exclude :name => "tasks/**"
89
89
  end
90
90
  if args[:include_dir]
91
+ dirs = args[:include_dir].split(":")
92
+
93
+ # first add any resources/ dir in the tree in the jar root - requirement for ShellBolt multilang resources
94
+ dirs.each do |dir|
95
+ resources_dirs = Dir.glob("#{dir}/**/resources")
96
+ resources_dirs.each do |resources_dir|
97
+ resources_parent = resources_dir.gsub("/resources", "")
98
+ fileset :dir => resources_parent do
99
+ include :name => "resources/**/*"
100
+ end
101
+ end
102
+ end
103
+
104
+ # include complete source dir tree (note we don't care about potential duplicated resources dir)
91
105
  fileset :dir => CWD do
92
- args[:include_dir].split(":").each{|dir| include :name => "#{dir}/**/*"}
106
+ dirs.each{|dir| include :name => "#{dir}/**/*"}
93
107
  end
94
108
  end
95
109
  manifest do
@@ -157,8 +171,8 @@ end
157
171
 
158
172
  task :bundle, [:groups] => :setup do |t, args|
159
173
  require 'bundler'
160
- args.with_defaults(:groups => 'default')
161
- groups = args[:groups].split(':').map(&:to_sym)
174
+ defaulted_args = {:groups => 'default'}.merge(args.to_hash.delete_if{|k, v| v.to_s.empty?})
175
+ groups = defaulted_args[:groups].split(':').map(&:to_sym)
162
176
  Bundler.definition.specs_for(groups).each do |spec|
163
177
  unless spec.full_name =~ /^bundler-\d+/
164
178
  destination_path = "#{TARGET_GEM_DIR}/#{spec.full_name}"
@@ -5,6 +5,7 @@ import backtype.storm.task.TopologyContext;
5
5
  import backtype.storm.topology.IRichBolt;
6
6
  import backtype.storm.topology.OutputFieldsDeclarer;
7
7
  import backtype.storm.tuple.Tuple;
8
+ import backtype.storm.tuple.Fields;
8
9
  import java.util.Map;
9
10
 
10
11
  /**
@@ -20,16 +21,19 @@ import java.util.Map;
20
21
  public class JRubyBolt implements IRichBolt {
21
22
  IRichBolt _proxyBolt;
22
23
  String _realBoltClassName;
23
- String _baseClassPath;
24
+ String _baseClassPath;
25
+ String[] _fields;
26
+
24
27
  /**
25
28
  * create a new JRubyBolt
26
29
  *
27
30
  * @param baseClassPath the topology/project base JRuby class file path
28
31
  * @param realBoltClassName the fully qualified JRuby bolt implementation class name
29
32
  */
30
- public JRubyBolt(String baseClassPath, String realBoltClassName) {
33
+ public JRubyBolt(String baseClassPath, String realBoltClassName, String[] fields) {
31
34
  _baseClassPath = baseClassPath;
32
35
  _realBoltClassName = realBoltClassName;
36
+ _fields = fields;
33
37
  }
34
38
 
35
39
  @Override
@@ -54,8 +58,12 @@ public class JRubyBolt implements IRichBolt {
54
58
  // declareOutputFields is executed in the topology creation time, before serialisation.
55
59
  // do not set the _proxyBolt instance variable here to avoid JRuby serialization
56
60
  // issues. Just create tmp bolt instance to call declareOutputFields.
57
- IRichBolt bolt = newProxyBolt(_baseClassPath, _realBoltClassName);
58
- bolt.declareOutputFields(declarer);
61
+ if (_fields.length > 0) {
62
+ declarer.declare(new Fields(_fields));
63
+ } else {
64
+ IRichBolt bolt = newProxyBolt(_baseClassPath, _realBoltClassName);
65
+ bolt.declareOutputFields(declarer);
66
+ }
59
67
  }
60
68
 
61
69
  @Override
@@ -0,0 +1,26 @@
1
+ package redstorm.storm.jruby;
2
+
3
+ import backtype.storm.task.ShellBolt;
4
+ import backtype.storm.topology.IRichBolt;
5
+ import backtype.storm.topology.OutputFieldsDeclarer;
6
+ import backtype.storm.tuple.Fields;
7
+ import java.util.Map;
8
+
9
+ public class JRubyShellBolt extends ShellBolt implements IRichBolt {
10
+ private String[] _fields;
11
+
12
+ public JRubyShellBolt(String[] command, String[] fields) {
13
+ super(command);
14
+ _fields = fields;
15
+ }
16
+
17
+ @Override
18
+ public void declareOutputFields(OutputFieldsDeclarer declarer) {
19
+ declarer.declare(new Fields(_fields));
20
+ }
21
+
22
+ @Override
23
+ public Map<String, Object> getComponentConfiguration() {
24
+ return null;
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ package backtype.storm.clojure;
2
+
3
+ import backtype.storm.spout.ShellSpout;
4
+ import backtype.storm.topology.IRichSpout;
5
+ import backtype.storm.topology.OutputFieldsDeclarer;
6
+ import backtype.storm.tuple.Fields;
7
+ import java.util.Map;
8
+
9
+ public class JRubyShellSpout extends ShellSpout implements IRichSpout {
10
+ private String[] _fields;
11
+
12
+ public JRubyShellSpout(String[] command, String[] fields) {
13
+ super(command);
14
+ _fields = fields;
15
+ }
16
+
17
+ @Override
18
+ public void declareOutputFields(OutputFieldsDeclarer declarer) {
19
+ declarer.declare(new Fields(_fields));
20
+ }
21
+
22
+ @Override
23
+ public Map<String, Object> getComponentConfiguration() {
24
+ return null;
25
+ }
26
+ }
@@ -5,6 +5,7 @@ import backtype.storm.task.TopologyContext;
5
5
  import backtype.storm.topology.IRichSpout;
6
6
  import backtype.storm.topology.OutputFieldsDeclarer;
7
7
  import backtype.storm.tuple.Tuple;
8
+ import backtype.storm.tuple.Fields;
8
9
  import java.util.Map;
9
10
 
10
11
  /**
@@ -21,16 +22,18 @@ public class JRubySpout implements IRichSpout {
21
22
  IRichSpout _proxySpout;
22
23
  String _realSpoutClassName;
23
24
  String _baseClassPath;
24
-
25
+ String[] _fields;
26
+
25
27
  /**
26
28
  * create a new JRubySpout
27
29
  *
28
30
  * @param baseClassPath the topology/project base JRuby class file path
29
31
  * @param realSpoutClassName the fully qualified JRuby spout implementation class name
30
32
  */
31
- public JRubySpout(String baseClassPath, String realSpoutClassName) {
33
+ public JRubySpout(String baseClassPath, String realSpoutClassName, String[] fields) {
32
34
  _baseClassPath = baseClassPath;
33
35
  _realSpoutClassName = realSpoutClassName;
36
+ _fields = fields;
34
37
  }
35
38
 
36
39
  @Override
@@ -75,8 +78,12 @@ public class JRubySpout implements IRichSpout {
75
78
  // declareOutputFields is executed in the topology creation time before serialisation.
76
79
  // do not set the _proxySpout instance variable here to avoid JRuby serialization
77
80
  // issues. Just create tmp spout instance to call declareOutputFields.
78
- IRichSpout spout = newProxySpout(_baseClassPath, _realSpoutClassName);
79
- spout.declareOutputFields(declarer);
81
+ if (_fields.length > 0) {
82
+ declarer.declare(new Fields(_fields));
83
+ } else {
84
+ IRichSpout spout = newProxySpout(_baseClassPath, _realSpoutClassName);
85
+ spout.declareOutputFields(declarer);
86
+ }
80
87
  }
81
88
 
82
89
  @Override
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: redstorm
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.6.2
5
+ version: 0.6.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Colin Surprenant
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-10 00:00:00.000000000 Z
12
+ date: 2012-10-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -91,6 +91,9 @@ files:
91
91
  - examples/native/random_sentence_spout.rb
92
92
  - examples/native/split_sentence_bolt.rb
93
93
  - examples/native/word_count_bolt.rb
94
+ - examples/shell/shell_topology.rb
95
+ - examples/shell/resources/splitsentence.py
96
+ - examples/shell/resources/storm.py
94
97
  - examples/simple/exclamation_bolt.rb
95
98
  - examples/simple/exclamation_topology.rb
96
99
  - examples/simple/exclamation_topology2.rb
@@ -102,6 +105,8 @@ files:
102
105
  - examples/simple/word_count_bolt.rb
103
106
  - examples/simple/word_count_topology.rb
104
107
  - src/main/redstorm/storm/jruby/JRubyBolt.java
108
+ - src/main/redstorm/storm/jruby/JRubyShellBolt.java
109
+ - src/main/redstorm/storm/jruby/JRubyShellSpout.java
105
110
  - src/main/redstorm/storm/jruby/JRubySpout.java
106
111
  - bin/redstorm
107
112
  - Rakefile