norikra 1.0.8-java → 1.1.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: 917e9b3428179553a0f1d8bbc55281efa8cc00d5
4
- data.tar.gz: 95fa55e7b1918aad040c11bbd6b4b949fd3b9ab9
3
+ metadata.gz: 2ba35f8ca734eabab40ce673fcc67c6ad96bbba4
4
+ data.tar.gz: ebad6924402dfe676a74e6ee3b97762c30c9f060
5
5
  SHA512:
6
- metadata.gz: 21d08b2da70b8515c6d0b3aca18b1f62e3b29656a33a641ad8eabb4cef12789c1c94ffc7a2c35e7fa6a01257c2159b35c6bbdcd7a8d5a69dedd45b81118c2a4a
7
- data.tar.gz: fe5f74066d60bf5bf87b061293cc95ab6ac4f6bd35bd1811a4e4dfe3c0d5bb4287d74ada2d5e12b9c208765e768c97cb193bb5c448625f96eaaa5156d1c3edfa
6
+ metadata.gz: be637b070e42b213a675aac50e1ec38f180f1aa1dbb5b03e6b68e14d4275466ea4b69b2f5cdfb6a6fe75efa0b04105aeb33f3b8806179e6db47586837409c1ea
7
+ data.tar.gz: de613937a954073acaf0c8493a16844109148e71584b6aa3730f9e4208326db921f87203e5bf8a6d5b1f13c90216d95d540b92ae7a7192fe85f429f13eaf691b
@@ -16,7 +16,7 @@ require 'norikra/typedef_manager'
16
16
 
17
17
  module Norikra
18
18
  class Engine
19
- attr_reader :targets, :queries, :output_pool, :typedef_manager
19
+ attr_reader :targets, :queries, :suspended_queries, :output_pool, :typedef_manager
20
20
 
21
21
  def initialize(output_pool, typedef_manager, opts={})
22
22
  @statistics = {
@@ -38,6 +38,7 @@ module Norikra
38
38
 
39
39
  @targets = []
40
40
  @queries = []
41
+ @suspended_queries = []
41
42
 
42
43
  @waiting_queries = []
43
44
  end
@@ -169,6 +170,7 @@ module Norikra
169
170
  def register(query)
170
171
  info "registering query", :name => query.name, :targets => query.targets, :expression => query.expression
171
172
  raise Norikra::ClientError, "query name '#{query.name}' already exists" if @queries.select{|q| q.name == query.name }.size > 0
173
+ raise Norikra::ClientError, "query name '#{query.name}' already exists in suspended" if @suspended_queries.select{|q| q.name == query.name }.size > 0
172
174
  raise Norikra::ClientError, "query '#{query.name}' is invalid query for Norikra" if query.invalid?
173
175
 
174
176
  query.targets.each do |target_name|
@@ -180,9 +182,43 @@ module Norikra
180
182
  def deregister(query_name)
181
183
  info "de-registering query", :name => query_name
182
184
  queries = @queries.select{|q| q.name == query_name }
185
+ s_queries = @suspended_queries.select{|q| q.name == query_name }
186
+
187
+ if queries.size == 1
188
+ deregister_query(queries.first)
189
+ elsif s_queries.size == 1
190
+ @suspended_queries.delete(s_queries.first)
191
+ true
192
+ else
193
+ nil # just ignore for 'not found'
194
+ end
195
+ end
196
+
197
+ def suspend(query_name)
198
+ info "suspending query", name: query_name
199
+ queries = @queries.select{|q| q.name == query_name }
183
200
  return nil unless queries.size == 1 # just ignore for 'not found'
184
201
 
185
- deregister_query(queries.first)
202
+ suspending_query = queries.first
203
+ suspended_query = Norikra::SuspendedQuery.new(suspending_query)
204
+
205
+ deregister_query(suspending_query)
206
+ add_suspended_query(suspended_query)
207
+ end
208
+
209
+ def resume(query_name)
210
+ info "resuming query", name: query_name
211
+ queries = @suspended_queries.select{|q| q.name == query_name }
212
+ return nil unless queries.size == 1 # just ignore
213
+
214
+ suspended_query = queries.first
215
+ query = suspended_query.create # suspended query -> query object
216
+
217
+ query.targets.each do |target_name|
218
+ open(target_name) unless @targets.any?{|t| t.name == target_name}
219
+ end
220
+ register_query(query)
221
+ remove_suspended_query(suspended_query)
186
222
  end
187
223
 
188
224
  def send(target_name, events)
@@ -359,6 +395,21 @@ module Norikra
359
395
  true
360
396
  end
361
397
 
398
+ def add_suspended_query(query)
399
+ @mutex.synchronize do
400
+ return nil if @suspended_queries.include?(query)
401
+ @suspended_queries << query
402
+ end
403
+ true
404
+ end
405
+
406
+ def remove_suspended_query(query)
407
+ @mutex.synchronize do
408
+ @suspended_queries.delete(query)
409
+ end
410
+ true
411
+ end
412
+
362
413
  def register_waiting_queries
363
414
  ready = []
364
415
  not_ready = []
@@ -421,6 +472,8 @@ module Norikra
421
472
 
422
473
  listener = if Norikra::Query.loopback(query.group)
423
474
  Norikra::LoopbackListener.new(self, query.name, query.group, @statistics[:events])
475
+ elsif Norikra::Query.stdout?(query.group)
476
+ Norikra::StdoutListener.new(self, query.name, query.group, @statistics[:events])
424
477
  else
425
478
  Norikra::Listener.new(query.name, query.group, @output_pool, @statistics[:events])
426
479
  end
@@ -7,6 +7,8 @@ require 'esper/lib/cglib-nodep-3.1.jar'
7
7
  require 'norikra/field'
8
8
  require 'norikra/query'
9
9
 
10
+ require 'json'
11
+
10
12
  module Norikra
11
13
  class Listener
12
14
  include com.espertech.esper.client.UpdateListener
@@ -67,6 +69,29 @@ module Norikra
67
69
  end
68
70
  end
69
71
 
72
+ class StdoutListener < Listener
73
+ def initialize(engine, query_name, query_group, events_statistics)
74
+ raise "BUG: query group is not 'STDOUT()'" unless query_group == 'STDOUT()'
75
+
76
+ @engine = engine
77
+ @query_name = query_name
78
+ @query_group = query_group
79
+ @events_statistics = events_statistics
80
+
81
+ @stdout = STDOUT
82
+ end
83
+
84
+ def update(new_events, old_events)
85
+ event_list = new_events.map{|e| type_convert(e) }
86
+ trace "stdout event", :query => @query_name, :event => event_list
87
+ @events_statistics[:output] += event_list.size
88
+
89
+ event_list.each do |e|
90
+ @stdout.puts @query_name + "\t" + JSON.dump(e)
91
+ end
92
+ end
93
+ end
94
+
70
95
  ##### Unmatched events are simply ignored
71
96
  # class UnmatchedListener
72
97
  # include com.espertech.esper.client.UnmatchedListener
@@ -49,18 +49,26 @@ module Norikra
49
49
  group && group =~ /^LOOPBACK\((.+)\)$/ && $1
50
50
  end
51
51
 
52
+ def self.stdout?(group)
53
+ group && group == "STDOUT()"
54
+ end
55
+
52
56
  def dup
53
57
  self.class.new(:name => @name, :group => @group, :expression => @expression.dup)
54
58
  end
55
59
 
56
60
  def to_hash
57
- {'name' => @name, 'group' => @group, 'expression' => @expression, 'targets' => self.targets}
61
+ {'name' => @name, 'group' => @group, 'expression' => @expression, 'targets' => self.targets, 'suspended' => false}
58
62
  end
59
63
 
60
64
  def dump
61
65
  {name: @name, group: @group, expression: @expression}
62
66
  end
63
67
 
68
+ def suspended?
69
+ false
70
+ end
71
+
64
72
  def invalid?
65
73
  # check query is invalid as Norikra query or not
66
74
  self.ast.listup('selectionListElement').any?{|node| node.children.map(&:name).any?{|name| name == '*' } }
@@ -457,4 +465,27 @@ module Norikra
457
465
  def dup; self; end
458
466
  def dup_with_stream_name(actual_name); self; end
459
467
  end
468
+
469
+ class SuspendedQuery
470
+ attr_accessor :name, :group, :expression, :targets
471
+
472
+ def initialize(query)
473
+ @name = query.name
474
+ @group = query.group
475
+ @expression = query.expression
476
+ @targets = query.targets
477
+ end
478
+
479
+ def suspended?
480
+ true
481
+ end
482
+
483
+ def to_hash
484
+ {'name' => @name, 'group' => @group, 'expression' => @expression, 'targets' => @targets, 'suspended' => true}
485
+ end
486
+
487
+ def create
488
+ Query.new(name: @name, group: @group, expression: @expression)
489
+ end
490
+ end
460
491
  end
@@ -61,7 +61,7 @@ class Norikra::RPC::Handler
61
61
 
62
62
  def queries
63
63
  logging(:show, :queries){
64
- @engine.queries.map(&:to_hash)
64
+ @engine.queries.map(&:to_hash) + @engine.suspended_queries.map(&:to_hash)
65
65
  }
66
66
  end
67
67
 
@@ -79,6 +79,20 @@ class Norikra::RPC::Handler
79
79
  }
80
80
  end
81
81
 
82
+ def suspend(query_name)
83
+ logging(:manage, :suspend, [query_name]){
84
+ r = @engine.suspend(query_name)
85
+ !!r
86
+ }
87
+ end
88
+
89
+ def resume(query_name)
90
+ logging(:manage, :suspend, [query_name]){
91
+ r = @engine.resume(query_name)
92
+ !!r
93
+ }
94
+ end
95
+
82
96
  def fields(target)
83
97
  logging(:show, :fields, [target]){
84
98
  @engine.typedef_manager.field_list(target)
@@ -1,3 +1,3 @@
1
1
  module Norikra
2
- VERSION = "1.0.8"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -66,7 +66,7 @@ class Norikra::WebUI::Handler < Sinatra::Base
66
66
 
67
67
  input_data,session[:input_data] = session[:input_data],nil
68
68
 
69
- queries = engine.queries.sort
69
+ queries = engine.queries.sort + engine.suspended_queries.sort
70
70
  pooled_events = Hash[* queries.map{|q| [q.name, engine.output_pool.pool.fetch(q.name, []).size.to_s]}.flatten]
71
71
 
72
72
  engine_targets = engine.targets.sort
@@ -130,6 +130,22 @@ class Norikra::WebUI::Handler < Sinatra::Base
130
130
  end
131
131
  end
132
132
 
133
+ post '/suspend' do
134
+ query_name = params[:query_name]
135
+ logging(:manage, :suspend, [query_name]) do
136
+ engine.suspend(query_name)
137
+ redirect '/#queries'
138
+ end
139
+ end
140
+
141
+ post '/resume' do
142
+ query_name = params[:query_name]
143
+ logging(:manage, :resume, [query_name]) do
144
+ engine.resume(query_name)
145
+ redirect '/#queries'
146
+ end
147
+ end
148
+
133
149
  get '/logs' do
134
150
  logging(:show, :logs) do
135
151
  json Norikra::Log.logger.buffer
@@ -149,6 +165,9 @@ class Norikra::WebUI::Handler < Sinatra::Base
149
165
  query_name = params[:name]
150
166
  logging(:show, :json_query, [query_name]) do
151
167
  query = engine.queries.select{|q| q.name == query_name}.first
168
+ unless query
169
+ query = engine.suspended_queries.select{|q| q.name == query_name}.first
170
+ end
152
171
  if query
153
172
  content = {
154
173
  name: query.name,
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency "rack"
24
24
  spec.add_runtime_dependency "thor"
25
25
  spec.add_runtime_dependency "msgpack-rpc-over-http-jruby", ">= 0.0.6"
26
- spec.add_runtime_dependency "norikra-client-jruby", ">= 1.0.0"
26
+ spec.add_runtime_dependency "norikra-client-jruby", ">= 1.1.0"
27
27
  spec.add_runtime_dependency "sinatra"
28
28
  spec.add_runtime_dependency "sinatra-contrib"
29
29
  spec.add_runtime_dependency "erubis"
@@ -20,6 +20,10 @@ body {
20
20
  margin: 5px 0;
21
21
  }
22
22
 
23
+ tr.suspended {
24
+ background-color: gray;
25
+ }
26
+
23
27
  tr.logline.trace > td {
24
28
  color: silver;
25
29
  }
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require_relative './spec_helper'
3
3
 
4
+ require 'json'
4
5
  require 'norikra/listener'
5
6
 
6
7
  class DummyOutputPool
@@ -161,3 +162,39 @@ describe Norikra::LoopbackListener do
161
162
  end
162
163
  end
163
164
  end
165
+
166
+ describe Norikra::StdoutListener do
167
+ it 'should be initialized' do
168
+ dummy_engine = DummyEngine.new
169
+ statistics = {output: 0}
170
+ expect { Norikra::StdoutListener.new(dummy_engine, 'name', 'STDOUT()', statistics) }.not_to raise_error
171
+ end
172
+
173
+ describe '#update' do
174
+ dummy_engine = DummyEngine.new
175
+ statistics = {output: 0}
176
+ listener = Norikra::StdoutListener.new(dummy_engine, 'name', 'STDOUT()', statistics)
177
+ dummyio = StringIO.new
178
+ listener.instance_eval{ @stdout = dummyio }
179
+
180
+ it 'sends events into engine with target name' do
181
+ dummyio.truncate(0)
182
+
183
+ events1 = [{"n1" => 100, "s" => "string one"}, {"n1" => 101, "s" => "string two"}]
184
+ listener.update(events1, [])
185
+ expect(statistics[:output]).to eql(2)
186
+
187
+ events2 = [{"n1" => 102, "s" => "string three"}]
188
+ listener.update(events2, [])
189
+ expect(statistics[:output]).to eql(3)
190
+
191
+ results = []
192
+ dummyio.string.split("\n").each do |line|
193
+ query_name, json = line.split("\t")
194
+ expect(query_name).to eql("name")
195
+ results << JSON.parse(json) if json && json != ''
196
+ end
197
+ expect(results).to eql(events1 + events2)
198
+ end
199
+ end
200
+ end
@@ -338,6 +338,16 @@ describe Norikra::Query do
338
338
  end
339
339
  end
340
340
 
341
+ describe '.stdout?' do
342
+ it 'returns true if group name is "STDOUT()"' do
343
+ expect(Norikra::Query.stdout?(nil)).to be_false
344
+ expect(Norikra::Query.stdout?("")).to be_false
345
+ expect(Norikra::Query.stdout?("foo")).to be_false
346
+ expect(Norikra::Query.stdout?("STDOUT")).to be_false
347
+ expect(Norikra::Query.stdout?("STDOUT()")).to be_true
348
+ end
349
+ end
350
+
341
351
  describe '#dup' do
342
352
  context 'for queries without group (default group)' do
343
353
  it 'returns query object with default group' do
@@ -362,6 +372,14 @@ describe Norikra::Query do
362
372
  end
363
373
  end
364
374
 
375
+ describe '#suspended?' do
376
+ it 'returns false, always' do
377
+ e1 = 'SELECT max(num) AS max FROM TestTable1.win:time(5 sec)'
378
+ q1 = Norikra::Query.new(:name => 'q1', :group => nil, :expression => e1)
379
+ expect(q1.suspended?).to be_false
380
+ end
381
+ end
382
+
365
383
  describe '.rewrite_event_field_name' do
366
384
  context 'without any container field access' do
367
385
  expression = 'select count(*) as cnt from TestTable.win:time_batch(10 seconds) where path="/" and size>100 and (param.length())>0'
@@ -742,3 +760,5 @@ describe Norikra::Query do
742
760
  end
743
761
  end
744
762
  end
763
+
764
+ #TODO: write specs about Norikra::SuspendedQuery
@@ -65,12 +65,15 @@
65
65
 
66
66
  <% if queries.size > 0 %>
67
67
  <table class="table">
68
- <tr><th>Group</th><th>Query name</th><th>Targets</th><th>Query</th><th style="text-align:right;">Events</th><th></th><th></th></tr>
69
- <% queries.each_with_index do |query, index| %>
70
68
  <tr>
69
+ <th>Group</th><th>Query name</th><th>Targets</th><th>Query</th><th style="text-align:right;">Events</th><th></th><th></th><th></th>
70
+ </tr>
71
+ <% queries.each_with_index do |query, index| %>
72
+ <tr class="<%= query.suspended? ? "suspended" : "" %>">
71
73
  <td><%= query.group || "(default)" %></td>
72
74
  <td><%= query.name %></td>
73
75
  <td><%= query.targets.join(", ") %></td>
76
+ <td><%= query.suspended? ? "suspended" : "" %></td>
74
77
  <td>
75
78
  <button class="btn btn-default btn-xs show-query-expression" data-load="/json/query/<%= query.name %>">show query</button>
76
79
  </td>
@@ -82,6 +85,63 @@
82
85
  </button>
83
86
  <% end %>
84
87
  </td>
88
+ <td>
89
+ <% if query.suspended? %>
90
+ <a class="btn btn-info btn-xs" data-toggle="modal" href="#resumeQuery<%= index %>">
91
+ <span class="glyphicon glyphicon-play"></span>
92
+ </a>
93
+ <div class="modal fade"
94
+ id="resumeQuery<%= index %>"
95
+ tabindex="-1" role="dialog" aria-labelledby="resumeQueryLabel<%= index %>" aria-hidden="true">
96
+ <div class="modal-dialog">
97
+ <div class="modal-content">
98
+ <div class="modal-header">
99
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
100
+ <h4 class="modal-title">Resume Query <%= query.name %></h4>
101
+ </div>
102
+ <div class="modal-body">
103
+ <p>name: <%= query.name %>, group: <%= query.group || "(default)" %></p>
104
+ <pre><%= query.expression %></pre>
105
+ </div>
106
+ <div class="modal-footer">
107
+ <form class="form-inline" action="/resume" method="POST">
108
+ <input type="hidden" name="query_name" value="<%= query.name %>" />
109
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
110
+ <button type="submit" class="btn btn-primary">Resume</button>
111
+ </form>
112
+ </div>
113
+ </div><!-- /.modal-content -->
114
+ </div><!-- /.modal-dialog -->
115
+ </div><!-- /.modal -->
116
+ <% else %>
117
+ <a class="btn btn-warning btn-xs" data-toggle="modal" href="#suspendQuery<%= index %>">
118
+ <span class="glyphicon glyphicon-pause"></span>
119
+ </a>
120
+ <div class="modal fade"
121
+ id="suspendQuery<%= index %>"
122
+ tabindex="-1" role="dialog" aria-labelledby="suspendQueryLabel<%= index %>" aria-hidden="true">
123
+ <div class="modal-dialog">
124
+ <div class="modal-content">
125
+ <div class="modal-header">
126
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
127
+ <h4 class="modal-title">Suspend Query <%= query.name %></h4>
128
+ </div>
129
+ <div class="modal-body">
130
+ <p>name: <%= query.name %>, group: <%= query.group || "(default)" %></p>
131
+ <pre><%= query.expression %></pre>
132
+ </div>
133
+ <div class="modal-footer">
134
+ <form class="form-inline" action="/suspend" method="POST">
135
+ <input type="hidden" name="query_name" value="<%= query.name %>" />
136
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
137
+ <button type="submit" class="btn btn-danger">Suspend</button>
138
+ </form>
139
+ </div>
140
+ </div><!-- /.modal-content -->
141
+ </div><!-- /.modal-dialog -->
142
+ </div><!-- /.modal -->
143
+ <% end %>
144
+ </td>
85
145
  <td>
86
146
  <a class="btn btn-danger btn-xs" data-toggle="modal" href="#removeQuery<%= index %>">
87
147
  <span class="glyphicon glyphicon-trash"></span>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: norikra
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.1.0
5
5
  platform: java
6
6
  authors:
7
7
  - TAGOMORI Satoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-14 00:00:00.000000000 Z
11
+ date: 2014-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mizuno
@@ -72,12 +72,12 @@ dependencies:
72
72
  requirements:
73
73
  - - '>='
74
74
  - !ruby/object:Gem::Version
75
- version: 1.0.0
75
+ version: 1.1.0
76
76
  requirement: !ruby/object:Gem::Requirement
77
77
  requirements:
78
78
  - - '>='
79
79
  - !ruby/object:Gem::Version
80
- version: 1.0.0
80
+ version: 1.1.0
81
81
  prerelease: false
82
82
  type: :runtime
83
83
  - !ruby/object:Gem::Dependency