norikra 1.1.2-java → 1.2.0-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90c6be383b83590b832fb6271a3642a42295ae6f
4
- data.tar.gz: 23555f2d09692459ef6fa17b85a47af61129e769
3
+ metadata.gz: e7e969ece3deb5788c7e7e76c9d6cc4961b4307c
4
+ data.tar.gz: bbfdc5a4056935c355082877e6ef347b7ded54ec
5
5
  SHA512:
6
- metadata.gz: b93442c3b3e202a880fa75f90065770b1b44b210789684b16889b24b7c10f91144488e6f2564b25e78ae0850c2a8f24c711e83c0c8b8d8a90608ee5c2e0b249e
7
- data.tar.gz: b34e828cd14c7c4fb00a2d83c17131590be520e15559c9caef5e429d46a92b311e027a12a283721de81000efbb12d79b752df3e1559a4890bef4cabf93788b2d
6
+ metadata.gz: 4ea0f4f04ba875e2432db51a179d75e3ae18e3a05fc03e945b14fdce187b61da969cd334e11d67aaf8a49b0a032b389f2f57a6c385c98f284b4ecd5bd3156156
7
+ data.tar.gz: 0add6a6f2539d188f2de82856f1d8cc222f9e6dd60b31b96acbc10167f047d9395c71273dde65a97d23b9b0838d30e0c7661b899d226077def93301e272515da
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- jruby-1.7.16.1
1
+ jruby-1.7.18
data/Changes.md CHANGED
@@ -3,6 +3,10 @@
3
3
  Changes of norikra.
4
4
 
5
5
  ## v1
6
+ * v1.2.0
7
+ * Add `NULLABLE(...)` field to query NULL explicitly
8
+ * Pluggable listener and user defined listener gems
9
+ * Fix bug to ignore fields in Esper built-in function
6
10
  * v1.1.2
7
11
  * Enable `-javaagent` option
8
12
  * Fix default/pre-set threads
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in norikra.gemspec
4
4
  gemspec
5
+
6
+ # to check plugin behaviors
7
+ gem "norikra-udf-mock"
8
+ gem "norikra-listener-mock"
data/README.md CHANGED
@@ -38,12 +38,6 @@ Fix code and tests:
38
38
  1. fix code in `lib`
39
39
  1. run `bundle exec rake`
40
40
 
41
- Run tests faster than 2 or more times:
42
-
43
- 1. execute `spork`
44
- 1. execute `script/spec_server_pry` in another terminal
45
- 1. run `rspec` in pry console (executed fastly after second times)
46
-
47
41
  Execute norikra server with target/query continuation:
48
42
 
49
43
  1. `bundle exec rake devserver`
@@ -70,6 +64,9 @@ See: http://norikra.github.io/
70
64
 
71
65
  ## Changes
72
66
 
67
+ * v1.2
68
+ * `NULLABLE()` fields
69
+ * Pluggable listeners
73
70
  * v1.1
74
71
  * Suspend/Resume queries
75
72
  * v1.0
data/lib/norikra/cli.rb CHANGED
@@ -11,70 +11,70 @@ module Norikra
11
11
  def self.register_common_start_options(klass)
12
12
  klass.module_exec {
13
13
  ### Server options
14
- option :host, :type => :string, :default => nil, :aliases => "-H", :desc => 'host address that server listen [0.0.0.0]'
15
- option :port, :type => :numeric, :default => nil, :aliases => "-P", :desc => 'port that server uses [26571]'
16
- option :'ui-port', :type => :numeric, :default => nil, :desc => 'port that Web UI server uses [26578]'
14
+ option :host, type: :string, default: nil, aliases: "-H", desc: 'host address that server listen [0.0.0.0]'
15
+ option :port, type: :numeric, default: nil, aliases: "-P", desc: 'port that server uses [26571]'
16
+ option :'ui-port', type: :numeric, default: nil, desc: 'port that Web UI server uses [26578]'
17
17
 
18
- option :stats, :type => :string, :default => nil, :aliases => "-s", \
19
- :desc => 'status file path to load/dump targets and queries [none]'
20
- option :'stats-secondary', :type => :string, :default => nil, \
21
- :desc => 'status file secondary path, to dump stats with date/time, like "stats.%Y%m%d.json" [none]'
22
- option :'suppress-dump-stat', :type => :boolean, :default => false, \
23
- :desc => 'specify not to update stat file with updated targets/queries on runtime [false]'
24
- option :'dump-stat-interval', :type => :numeric, :default => nil, \
25
- :desc => 'interval(seconds) of status file dumps on runtime [none (on shutdown only)]'
18
+ option :stats, type: :string, default: nil, aliases: "-s", \
19
+ desc: 'status file path to load/dump targets and queries [none]'
20
+ option :'stats-secondary', type: :string, default: nil, \
21
+ desc: 'status file secondary path, to dump stats with date/time, like "stats.%Y%m%d.json" [none]'
22
+ option :'suppress-dump-stat', type: :boolean, default: false, \
23
+ desc: 'specify not to update stat file with updated targets/queries on runtime [false]'
24
+ option :'dump-stat-interval', type: :numeric, default: nil, \
25
+ desc: 'interval(seconds) of status file dumps on runtime [none (on shutdown only)]'
26
26
 
27
27
  ### Daemonize options
28
- option :daemonize, :type => :boolean, :default => false, :aliases => "-d", \
29
- :desc => 'daemonize Norikra server [false (foreground)]'
30
- option :pidfile, :type => :string, :default => DEFAULT_PID_PATH, :aliases => "-p", \
31
- :desc => "pidfile path when daemonized [#{DEFAULT_PID_PATH}]"
32
- option :outfile, :type => :string, :default => nil, \
33
- :desc => "stdout redirect file when daemonized [${logdir}/norikra.out]"
28
+ option :daemonize, type: :boolean, default: false, aliases: "-d", \
29
+ desc: 'daemonize Norikra server [false (foreground)]'
30
+ option :pidfile, type: :string, default: DEFAULT_PID_PATH, aliases: "-p", \
31
+ desc: "pidfile path when daemonized [#{DEFAULT_PID_PATH}]"
32
+ option :outfile, type: :string, default: nil, \
33
+ desc: "stdout redirect file when daemonized [${logdir}/norikra.out]"
34
34
 
35
35
  ### JVM options
36
- option :'bare-jvm', :type => :boolean, :default => false, :desc => "use JVM without any recommended options"
37
- option :'gc-log', :type => :string, :default => nil, :desc => "output gc logs on specified file path"
36
+ option :'bare-jvm', type: :boolean, default: false, desc: "use JVM without any recommended options"
37
+ option :'gc-log', type: :string, default: nil, desc: "output gc logs on specified file path"
38
38
 
39
39
  ### Performance options
40
40
  # performance predefined configuration sets
41
- option :micro, :type => :boolean, :default => false, \
42
- :desc => 'development or testing (inbound:0, outbound:0, route:0, timer:0, rpc:2)'
43
- option :small, :type => :boolean, :default => false, \
44
- :desc => 'virtual or small scale servers (inbound:1, outbount:1, route:1, timer:1, rpc:2)'
45
- option :middle, :type => :boolean, :default => false, \
46
- :desc => 'rackmount servers (inbound:4, outbound:2, route:2, timer:2, rpc:4)'
47
- option :large, :type => :boolean, :default => false, \
48
- :desc => 'high performance servers (inbound: 6, outbound: 6, route:4, timer:4, rpc: 8)'
41
+ option :micro, type: :boolean, default: false, \
42
+ desc: 'development or testing (inbound:0, outbound:0, route:0, timer:0, rpc:2)'
43
+ option :small, type: :boolean, default: false, \
44
+ desc: 'virtual or small scale servers (inbound:1, outbount:1, route:1, timer:1, rpc:2)'
45
+ option :middle, type: :boolean, default: false, \
46
+ desc: 'rackmount servers (inbound:4, outbound:2, route:2, timer:2, rpc:4)'
47
+ option :large, type: :boolean, default: false, \
48
+ desc: 'high performance servers (inbound: 6, outbound: 6, route:4, timer:4, rpc: 8)'
49
49
  # Esper
50
- option :'inbound-threads', :type => :numeric, :default => nil, :desc => 'number of threads for inbound data'
51
- option :'outbound-threads', :type => :numeric, :default => nil, :desc => 'number of threads for outbound data'
52
- option :'route-threads', :type => :numeric, :default => nil, :desc => 'number of threads for events routing for query execution'
53
- option :'timer-threads', :type => :numeric, :default => nil, :desc => 'number of threads for internal timers for query execution'
50
+ option :'inbound-threads', type: :numeric, default: nil, desc: 'number of threads for inbound data'
51
+ option :'outbound-threads', type: :numeric, default: nil, desc: 'number of threads for outbound data'
52
+ option :'route-threads', type: :numeric, default: nil, desc: 'number of threads for events routing for query execution'
53
+ option :'timer-threads', type: :numeric, default: nil, desc: 'number of threads for internal timers for query execution'
54
54
  ### about capacity options of esper's capacity-bound queue processing, see Esper's thread reference.
55
55
  # http://esper.codehaus.org/esper-4.10.0/doc/reference/en-US/html/configuration.html#config-engine-threading-advanced
56
56
  # default nil: unbound queueing
57
- option :'inbound-thread-capacity', :type => :numeric, :default => nil
58
- option :'outbound-thread-capacity', :type => :numeric, :default => nil
59
- option :'route-thread-capacity', :type => :numeric, :default => nil
60
- option :'timer-thread-capacity', :type => :numeric, :default => nil
57
+ option :'inbound-thread-capacity', type: :numeric, default: nil
58
+ option :'outbound-thread-capacity', type: :numeric, default: nil
59
+ option :'route-thread-capacity', type: :numeric, default: nil
60
+ option :'timer-thread-capacity', type: :numeric, default: nil
61
61
  # Jetty
62
- option :'rpc-threads', :type => :numeric, :default => nil, :desc => 'number of threads for rpc handlers'
63
- option :'web-threads', :type => :numeric, :default => nil, :desc => 'number of threads for WebUI handlers'
62
+ option :'rpc-threads', type: :numeric, default: nil, desc: 'number of threads for rpc handlers'
63
+ option :'web-threads', type: :numeric, default: nil, desc: 'number of threads for WebUI handlers'
64
64
 
65
65
  ### Logging options
66
- option :logdir, :type => :string, :default => nil, :aliases => "-l", \
67
- :desc => "directory path of logfiles when daemonized [nil (console for foreground)]"
68
- option :'log-filesize', :type => :string, :default => nil, :desc => 'log rotation size [10MB]'
69
- option :'log-backups' , :type => :numeric, :default => nil, :desc => 'log rotation backups [10]'
70
- option :'log-buffer-lines', :type => :numeric, :default => nil, :desc => 'log lines to fetch from API [1000]'
71
- option :'log4j-properties-path', :type => :string, :default => nil, :desc => 'path to log4j.properties. ignore other log* options when this option is present'
66
+ option :logdir, type: :string, default: nil, aliases: "-l", \
67
+ desc: "directory path of logfiles when daemonized [nil (console for foreground)]"
68
+ option :'log-filesize', type: :string, default: nil, desc: 'log rotation size [10MB]'
69
+ option :'log-backups' , type: :numeric, default: nil, desc: 'log rotation backups [10]'
70
+ option :'log-buffer-lines', type: :numeric, default: nil, desc: 'log lines to fetch from API [1000]'
71
+ option :'log4j-properties-path', type: :string, default: nil, desc: 'path to log4j.properties. ignore other log* options when this option is present'
72
72
 
73
73
  ### Loglevel options
74
- option :'more-quiet', :type => :boolean, :default => false, :desc => 'set loglevel as ERROR'
75
- option :quiet, :type => :boolean, :default => false, :aliases => "-q", :desc => 'set loglevel as WARN'
76
- option :verbose, :type => :boolean, :default => false, :aliases => "-v", :desc => 'set loglevel as DEBUG'
77
- option :'more-verbose', :type => :boolean, :default => false, :desc => 'set loglevel as TRACE'
74
+ option :'more-quiet', type: :boolean, default: false, desc: 'set loglevel as ERROR'
75
+ option :quiet, type: :boolean, default: false, aliases: "-q", desc: 'set loglevel as WARN'
76
+ option :verbose, type: :boolean, default: false, aliases: "-v", desc: 'set loglevel as DEBUG'
77
+ option :'more-verbose', type: :boolean, default: false, desc: 'set loglevel as TRACE'
78
78
  }
79
79
  end
80
80
  end
@@ -117,7 +117,7 @@ module Norikra
117
117
  ### 'start' and 'serverprocess' have almost same option set (for parse/help)
118
118
  ### DIFF: jvm options (-X)
119
119
  Norikra::CLIUtil.register_common_start_options(self)
120
- option :help, :type => :boolean, :default => false, :aliases => "-h", :desc => "show this message"
120
+ option :help, type: :boolean, default: false, aliases: "-h", desc: "show this message"
121
121
  desc "start [-Xxxx] [other options]", "Start Norikra server process"
122
122
  def start(*optargs)
123
123
  if options[:help]
@@ -168,7 +168,7 @@ module Norikra
168
168
  File.open(outfile, 'w'){|file| file.write 'write test on parent process'}
169
169
 
170
170
  pidfile = File.open(options[:pidfile], 'w')
171
- pid = spawn(jruby_path, *args, :pgroup => 0)
171
+ pid = spawn(jruby_path, *args, pgroup: 0)
172
172
  pidfile.write(pid.to_s)
173
173
  pidfile.close
174
174
  waiting_child = true
@@ -183,12 +183,12 @@ module Norikra
183
183
  end
184
184
 
185
185
  Norikra::CLIUtil.register_common_start_options(self)
186
- desc "serverproc", "execute server process actually (don't execute this subcommand directly)", :hide => true
186
+ desc "serverproc", "execute server process actually (don't execute this subcommand directly)", hide: true
187
187
  def serverproc
188
188
  conf = {}
189
189
 
190
190
  if options[:daemonize]
191
- conf[:daemonize] = {:outfile => options[:outfile]}
191
+ conf[:daemonize] = {outfile: options[:outfile]}
192
192
  end
193
193
 
194
194
  ### stat file
@@ -247,9 +247,9 @@ module Norikra
247
247
  server.shutdown
248
248
  end
249
249
 
250
- option :pidfile, :type => :string, :default => DEFAULT_PID_PATH, :aliases => "-p", \
251
- :desc => "pidfile path when daemonized [#{DEFAULT_PID_PATH}]"
252
- option :timeout, :type => :numeric, :default => 5, :desc => "timeout seconds to wait process exit [5]"
250
+ option :pidfile, type: :string, default: DEFAULT_PID_PATH, aliases: "-p", \
251
+ desc: "pidfile path when daemonized [#{DEFAULT_PID_PATH}]"
252
+ option :timeout, type: :numeric, default: 5, desc: "timeout seconds to wait process exit [5]"
253
253
  desc "stop [options]", "stop daemonized Norikra server"
254
254
  def stop
255
255
  unless test(?r,options[:pidfile])
@@ -44,6 +44,9 @@ module Norikra
44
44
  @suspended_queries = []
45
45
 
46
46
  @waiting_queries = []
47
+
48
+ @listeners = []
49
+ @running_listeners = {} # query_name => listener
47
50
  end
48
51
 
49
52
  def statistics
@@ -109,7 +112,7 @@ module Norikra
109
112
 
110
113
  threads = t[sym][:threads].to_i
111
114
  capacity = t[sym][:capacity].to_i
112
- info "Engine #{sym} thread pool enabling", :threads => threads, :capacity => (capacity == 0 ? 'default' : capacity)
115
+ info "Engine #{sym} thread pool enabling", threads: threads, capacity: (capacity == 0 ? 'default' : capacity)
113
116
 
114
117
  cam = camelize(sym)
115
118
  threading.send("setThreadPool#{cam}".to_sym, true)
@@ -138,7 +141,7 @@ module Norikra
138
141
  def open(target_name, fields=nil, auto_field=true)
139
142
  # fields nil || [] => lazy
140
143
  # fields {'fieldname' => 'type'} : type 'string', 'boolean', 'int', 'long', 'float', 'double'
141
- info "opening target", :target => target_name, :fields => fields, :auto_field => auto_field
144
+ info "opening target", target: target_name, fields: fields, auto_field: auto_field
142
145
  raise Norikra::ArgumentError, "invalid target name" unless Norikra::Target.valid?(target_name)
143
146
  target = Norikra::Target.new(target_name, fields, auto_field)
144
147
  return false if @targets.include?(target)
@@ -146,7 +149,7 @@ module Norikra
146
149
  end
147
150
 
148
151
  def close(target_name)
149
- info "closing target", :target => target_name
152
+ info "closing target", target: target_name
150
153
  targets = @targets.select{|t| t.name == target_name}
151
154
  return false if targets.size != 1
152
155
  target = targets.first
@@ -157,7 +160,7 @@ module Norikra
157
160
  end
158
161
 
159
162
  def modify(target_name, auto_field)
160
- info "modify target", :target => target_name, :auto_field => auto_field
163
+ info "modify target", target: target_name, auto_field: auto_field
161
164
  targets = @targets.select{|t| t.name == target_name}
162
165
  if targets.size != 1
163
166
  raise Norikra::ArgumentError, "target name '#{target_name}' not found"
@@ -171,10 +174,12 @@ module Norikra
171
174
  end
172
175
 
173
176
  def register(query)
174
- info "registering query", :name => query.name, :targets => query.targets, :expression => query.expression
177
+ info "registering query", name: query.name, targets: query.targets, expression: query.expression
175
178
  raise Norikra::ClientError, "query name '#{query.name}' already exists" if @queries.select{|q| q.name == query.name }.size > 0
176
179
  raise Norikra::ClientError, "query name '#{query.name}' already exists in suspended" if @suspended_queries.select{|q| q.name == query.name }.size > 0
177
- raise Norikra::ClientError, "query '#{query.name}' is invalid query for Norikra" if query.invalid?
180
+ if reason = query.invalid?
181
+ raise Norikra::ClientError, "invalid query '#{query.name}': #{reason}"
182
+ end
178
183
 
179
184
  query.targets.each do |target_name|
180
185
  open(target_name) unless @targets.any?{|t| t.name == target_name}
@@ -183,7 +188,7 @@ module Norikra
183
188
  end
184
189
 
185
190
  def deregister(query_name)
186
- info "de-registering query", :name => query_name
191
+ info "de-registering query", name: query_name
187
192
  queries = @queries.select{|q| q.name == query_name }
188
193
  s_queries = @suspended_queries.select{|q| q.name == query_name }
189
194
 
@@ -242,12 +247,12 @@ module Norikra
242
247
  end
243
248
 
244
249
  def send(target_name, events)
245
- trace "send messages", :target => target_name, :events => events
250
+ trace "send messages", target: target_name, events: events
246
251
 
247
252
  @statistics[:events][:input] += events.size
248
253
 
249
254
  unless @targets.any?{|t| t.name == target_name} # discard events for target not registered
250
- trace "messages skipped for non-opened target", :target => target_name
255
+ trace "messages skipped for non-opened target", target: target_name
251
256
  return
252
257
  end
253
258
  return if events.size < 1
@@ -255,19 +260,19 @@ module Norikra
255
260
  target = @targets.select{|t| t.name == target_name}.first
256
261
 
257
262
  if @typedef_manager.lazy?(target.name)
258
- info "opening lazy target", :target => target
263
+ info "opening lazy target", target: target
259
264
 
260
265
  first_event = event_filter(events.first)
261
266
  if first_event.nil? # non-hash object
262
267
  raise Norikra::ClientError, "Input data must be JSON object"
263
268
  end
264
- debug "generating base fieldset from event", :target => target.name, :event => first_event
269
+ debug "generating base fieldset from event", target: target.name, event: first_event
265
270
  base_fieldset = @typedef_manager.generate_base_fieldset(target.name, first_event)
266
271
 
267
- debug "registering base fieldset", :target => target.name, :base => base_fieldset
272
+ debug "registering base fieldset", target: target.name, base: base_fieldset
268
273
  register_base_fieldset(target.name, base_fieldset)
269
274
 
270
- info "target successfully opened with fieldset", :target => target, :base => base_fieldset
275
+ info "target successfully opened with fieldset", target: target, base: base_fieldset
271
276
  end
272
277
 
273
278
  registered_data_fieldset = @registered_fieldsets[target_name][:data]
@@ -282,18 +287,19 @@ module Norikra
282
287
 
283
288
  unless registered_data_fieldset[fieldset.summary]
284
289
  # register waiting queries including this fieldset, and this fieldset itself
285
- debug "registering unknown fieldset", :target => target_name, :fieldset => fieldset
290
+ debug "registering unknown fieldset", target: target_name, fieldset: fieldset
286
291
  register_fieldset(target_name, fieldset)
287
292
  debug "successfully registered"
288
293
 
289
294
  # fieldset should be refined, when waiting_queries rewrite inheritance structure and data fieldset be renewed.
290
295
  fieldset = @typedef_manager.refer(target_name, event, strict_refer)
296
+ debug "re-referred data fieldset", target: target_name, fieldset: fieldset
291
297
  end
292
298
 
293
- trace "calling sendEvent with bound fieldset (w/ valid event_type_name)", :target => target_name, :event => event
294
- trace "This is assert for valid event_type_name", :event_type_name => fieldset.event_type_name
299
+ trace "calling sendEvent with bound fieldset (w/ valid event_type_name)", target: target_name, event: event
300
+ trace("This is assert for valid event_type_name"){ { event_type_name: fieldset.event_type_name } }
295
301
  formed = fieldset.format(event)
296
- trace "sendEvent", :data => formed
302
+ trace "sendEvent", data: formed
297
303
  @runtime.sendEvent(formed.to_java, fieldset.event_type_name)
298
304
  end
299
305
  target.update!
@@ -301,8 +307,13 @@ module Norikra
301
307
  nil
302
308
  end
303
309
 
304
- def load(plugin_klass)
305
- load_udf(plugin_klass)
310
+ def load(type, plugin_klass)
311
+ case type
312
+ when :udf then load_udf(plugin_klass)
313
+ when :listener then load_listener(plugin_klass)
314
+ else
315
+ raise "BUG: unknown plugin type: #{type}"
316
+ end
306
317
  end
307
318
 
308
319
  private
@@ -312,7 +323,7 @@ module Norikra
312
323
  return false if @targets.include?(target)
313
324
 
314
325
  @typedef_manager.add_target(target.name, target.fields)
315
- @registered_fieldsets[target.name] = {:base => {}, :query => {}, :data => {}}
326
+ @registered_fieldsets[target.name] = {base: {}, query: {}, data: {}}
316
327
 
317
328
  unless @typedef_manager.lazy?(target.name)
318
329
  base_fieldset = @typedef_manager.base_fieldset(target.name)
@@ -352,7 +363,7 @@ module Norikra
352
363
  def update_inherits_graph(target_name, query_fieldset)
353
364
  # replace registered data fieldsets with new fieldset inherits this query fieldset
354
365
  @typedef_manager.supersets(target_name, query_fieldset).each do |set|
355
- rebound = set.rebind(true) # update event_type_name with new inheritations
366
+ rebound = set.rebind(true, query_fieldset) # update event_type_name with new inheritations & nullable fields
356
367
 
357
368
  register_fieldset_actually(target_name, rebound, :data, true) # replacing on esper engine
358
369
  @typedef_manager.replace_fieldset(target_name, set, rebound)
@@ -362,26 +373,28 @@ module Norikra
362
373
 
363
374
  def register_query(query)
364
375
 
365
- if lo_target_name = Norikra::Query.loopback(query.group)
376
+ if lo_target_name = Norikra::Listener::Loopback.check(query.group)
366
377
  raise "Invalid loopback target name should be checked before. THIS IS BUG." unless Norikra::Target.valid?(lo_target_name)
367
378
 
368
379
  target = Norikra::Target.new(lo_target_name)
369
380
  unless @targets.include?(target)
370
- info "opening loopback target", :target => lo_target_name
381
+ info "opening loopback target", target: lo_target_name
371
382
  open_target(target)
372
383
  end
373
384
  end
374
385
 
375
386
  @mutex.synchronize do
376
387
  raise Norikra::ClientError, "query '#{query.name}' already exists" unless @queries.select{|q| q.name == query.name }.empty?
377
- raise Norikra::ClientError, "query '#{query.name}' is invalid query for Norikra" if query.invalid?
378
- if lo_target_name = Norikra::Query.loopback(query.group)
388
+ if reason = query.invalid?
389
+ raise Norikra::ClientError, "invalid query '#{query.name}': #{reason}"
390
+ end
391
+ if lo_target_name = Norikra::Listener::Loopback.check(query.group)
379
392
  raise Norikra::ClientError, "loopback target '#{lo_target_name}'" unless Norikra::Target.valid?(lo_target_name)
380
393
  end
381
394
 
382
395
  unless @typedef_manager.ready?(query)
383
396
  @waiting_queries.push(query)
384
- trace "waiting query fields", :targets => query.targets, :fields => query.targets.map{|t| query.fields(t)}
397
+ trace("waiting query fields"){ { targets: query.targets, fields: query.targets.map{|t| query.fields(t)} } }
385
398
  @typedef_manager.register_waiting_fields(query)
386
399
  @queries.push(query)
387
400
  return
@@ -389,7 +402,9 @@ module Norikra
389
402
 
390
403
  mapping = @typedef_manager.generate_fieldset_mapping(query)
391
404
  mapping.each do |target_name, query_fieldset|
405
+ trace "binding query fieldset", fieldset: query_fieldset
392
406
  @typedef_manager.bind_fieldset(target_name, :query, query_fieldset)
407
+ trace "registering query fieldset", fieldset: query_fieldset
393
408
  register_fieldset_actually(target_name, query_fieldset, :query)
394
409
  update_inherits_graph(target_name, query_fieldset)
395
410
  query.fieldsets[target_name] = query_fieldset
@@ -453,7 +468,9 @@ module Norikra
453
468
  ready.each do |query|
454
469
  mapping = @typedef_manager.generate_fieldset_mapping(query)
455
470
  mapping.each do |target_name, query_fieldset|
471
+ trace "binding query fieldset for waiting query", query: query, target: target_name, fieldset: query_fieldset
456
472
  @typedef_manager.bind_fieldset(target_name, :query, query_fieldset)
473
+ trace "registering query fieldset", target: target_name, fieldset: query_fieldset
457
474
  register_fieldset_actually(target_name, query_fieldset, :query)
458
475
  update_inherits_graph(target_name, query_fieldset)
459
476
  query.fieldsets[target_name] = query_fieldset
@@ -464,12 +481,30 @@ module Norikra
464
481
 
465
482
  def register_fieldset(target_name, fieldset)
466
483
  @mutex.synchronize do
484
+ trace "binding data fieldset", fieldset: fieldset # to prepare pickup waiting queries by newly comming fields
467
485
  @typedef_manager.bind_fieldset(target_name, :data, fieldset)
468
486
 
469
487
  if @waiting_queries.size > 0
470
488
  register_waiting_queries
471
489
  end
472
- debug "registering data fieldset", :target => target_name, :fields => fieldset.fields
490
+
491
+ diff_nullable_fields = []
492
+
493
+ @typedef_manager.subsets(target_name, fieldset).each do |query_fieldset|
494
+ next unless query_fieldset.level == :query
495
+ # fill nullable fields of all required query fieldsets
496
+ diff_nullable_fields += fieldset.nullable_diff(query_fieldset)
497
+ end
498
+
499
+ unless diff_nullable_fields.empty?
500
+ trace "query fieldset has nullable diff", diff: diff_nullable_fields
501
+ fieldset.update(diff_nullable_fields, true) # nullable fields are always optional
502
+ trace "rebinding data fieldset w/ nullable fields", fieldset: fieldset
503
+ rebound = fieldset.rebind(false) # type_name is not required to be updated because it is not registered yet
504
+ @typedef_manager.replace_fieldset(target_name, fieldset, rebound)
505
+ fieldset = rebound
506
+ end
507
+ debug "registering data fieldset", target: target_name, fieldset: fieldset
473
508
  register_fieldset_actually(target_name, fieldset, :data)
474
509
  end
475
510
  end
@@ -485,6 +520,10 @@ module Norikra
485
520
  end
486
521
  end
487
522
 
523
+ def load_listener(listener_klass)
524
+ @listeners.push(listener_klass)
525
+ end
526
+
488
527
  # this method should be protected with @mutex lock
489
528
  def register_query_actually(query, mapping)
490
529
  # 'mapping' argument is {target => fieldset}
@@ -498,13 +537,19 @@ module Norikra
498
537
  statement_model = administrator.compileEPL(query.expression)
499
538
  Norikra::Query.rewrite_query(statement_model, event_type_name_map)
500
539
 
501
- listener = if Norikra::Query.loopback(query.group)
502
- Norikra::LoopbackListener.new(self, query.name, query.group, @statistics[:events])
503
- elsif Norikra::Query.stdout?(query.group)
504
- Norikra::StdoutListener.new(self, query.name, query.group, @statistics[:events])
505
- else
506
- Norikra::Listener.new(query.name, query.group, @output_pool, @statistics[:events])
507
- end
540
+ listener = nil
541
+ @listeners.each do |klass|
542
+ trace("checking listeners"){ {target: klass, result: klass.check(query.group)} }
543
+ if klass.check(query.group)
544
+ listener = klass.new(query.name, query.group, @statistics[:events])
545
+ break
546
+ end
547
+ end
548
+ raise "BUG: no listener is selected" unless listener
549
+ listener.engine = self if listener.respond_to?(:engine=)
550
+ listener.output_pool = @output_pool if listener.respond_to?(:output_pool=)
551
+ listener.start
552
+ @running_listeners[query.name] = listener
508
553
 
509
554
  epl = administrator.create(statement_model)
510
555
  epl.java_send :addListener, [com.espertech.esper.client.UpdateListener.java_class], listener
@@ -522,9 +567,11 @@ module Norikra
522
567
  epl = administrator.getStatement(query.statement_name)
523
568
  return unless epl
524
569
 
525
- @output_pool.remove(query.name, query.group)
526
570
  epl.stop unless epl.isStopped
527
571
  epl.destroy unless epl.isDestroyed
572
+ listener = @running_listeners.delete(query.name)
573
+ listener.shutdown
574
+ @output_pool.remove(query.name, query.group)
528
575
  end
529
576
 
530
577
  # this method should be protected with @mutex lock
@@ -537,15 +584,15 @@ module Norikra
537
584
  # .addEventType("AccountUpdate", accountUpdateDef, new String[] {"BaseUpdate"});
538
585
  case level
539
586
  when :base
540
- debug "add event type", :target => target_name, :level => 'base', :event_type => fieldset.event_type_name
587
+ debug("add event type"){ { target: target_name, level: 'base', event_type: fieldset.event_type_name } }
541
588
  @config.addEventType(fieldset.event_type_name, fieldset.definition)
542
589
  when :query
543
590
  base_name = @typedef_manager.base_fieldset(target_name).event_type_name
544
- debug "add event type", :target => target_name, :level => 'query', :event_type => fieldset.event_type_name, :base => base_name
591
+ debug("add event type"){ { target: target_name, level: 'query', event_type: fieldset.event_type_name, base: base_name } }
545
592
  @config.addEventType(fieldset.event_type_name, fieldset.definition, [base_name].to_java(:string))
546
- else
593
+ else # :data
547
594
  subset_names = @typedef_manager.subsets(target_name, fieldset).map(&:event_type_name)
548
- debug "add event type", :target => target_name, :level => 'data', :event_type => fieldset.event_type_name, :inherit => subset_names
595
+ debug("add event type"){ { target: target_name, level: 'data', event_type: fieldset.event_type_name, inherit: subset_names } }
549
596
  @config.addEventType(fieldset.event_type_name, fieldset.definition, subset_names.to_java(:string))
550
597
 
551
598
  @registered_fieldsets[target_name][level][fieldset.summary] = fieldset
@@ -559,7 +606,7 @@ module Norikra
559
606
 
560
607
  # DON'T check @registered_fieldsets[target_name][level][fieldset.summary]
561
608
  # removed fieldset should be already replaced with register_fieldset_actually w/ replace flag
562
- debug "remove event type", :target => target_name, :event_type => event_type_name
609
+ debug "remove event type", target: target_name, event_type: event_type_name
563
610
  @config.removeEventType(event_type_name, true)
564
611
  end
565
612
 
@@ -567,7 +614,7 @@ module Norikra
567
614
  FILTER_OPTIMIZABLE_ENUM = com.espertech.esper.client.ConfigurationPlugInSingleRowFunction::FilterOptimizable
568
615
 
569
616
  def load_udf_actually(udf)
570
- debug "importing class into config object", :name => udf.class.to_s
617
+ debug "importing class into config object", name: udf.class.to_s
571
618
 
572
619
  functionName, className, methodName = udf.definition
573
620
 
@@ -575,17 +622,17 @@ module Norikra
575
622
  filterOptimizable = udf.filter_optimizable ? FILTER_OPTIMIZABLE_ENUM::ENABLED : FILTER_OPTIMIZABLE_ENUM::DISABLED
576
623
  rethrowExceptions = udf.rethrow_exceptions
577
624
 
578
- debug "adding SingleRowFunction", :function => functionName, :javaClass => className, :javaMethod => methodName
625
+ debug "adding SingleRowFunction", function: functionName, javaClass: className, javaMethod: methodName
579
626
  @config.addPlugInSingleRowFunction(functionName, className, methodName, valueCache, filterOptimizable, rethrowExceptions)
580
627
  functionName
581
628
  end
582
629
 
583
630
  def load_udf_aggregation_actually(udf)
584
- debug "importing class into config object", :name => udf.class.to_s
631
+ debug "importing class into config object", name: udf.class.to_s
585
632
 
586
633
  functionName, factoryClassName = udf.definition
587
634
 
588
- debug "adding AggregationSingleFactory", :function => functionName, :javaClass => factoryClassName
635
+ debug "adding AggregationSingleFactory", function: functionName, javaClass: factoryClassName
589
636
  @config.addPlugInAggregationFunctionFactory(functionName, factoryClassName)
590
637
  functionName
591
638
  end