norikra 1.0.8-java → 1.1.0-java

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