norikra 0.0.7-java → 0.0.8-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.
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in norikra.gemspec
4
4
  gemspec
5
+
6
+ gem "norikra-udf-woothee", :git => "file:///Users/tagomoris/Documents/norikra-udf-woothee"
data/README.md CHANGED
@@ -86,6 +86,12 @@ And send more events:
86
86
 
87
87
  Query 'www.search' matches the last event automatically.
88
88
 
89
+ ## User Defined Functions
90
+
91
+ UDF as plugins over rubygems are available. For example, see 'norikra-udf-mock' repository.
92
+
93
+ TBD
94
+
89
95
  ## Versions
90
96
 
91
97
  TBD
@@ -94,7 +100,6 @@ TBD
94
100
 
95
101
  * daemonize
96
102
  * performance parameters
97
- * query unregister
98
103
 
99
104
  ## Copyright
100
105
 
@@ -123,6 +123,10 @@ module Norikra
123
123
  nil
124
124
  end
125
125
 
126
+ def load(plugin_klass)
127
+ load_udf(plugin_klass)
128
+ end
129
+
126
130
  class Listener
127
131
  include com.espertech.esper.client.UpdateListener
128
132
 
@@ -282,6 +286,17 @@ module Norikra
282
286
  end
283
287
  end
284
288
 
289
+ def load_udf(udf_klass)
290
+ udf_klass.init if udf_klass.respond_to?(:init)
291
+
292
+ udf = udf_klass.new
293
+ if udf.is_a? Norikra::UDF::SingleRow
294
+ load_udf_actually(udf)
295
+ elsif udf.is_a? Norikra::UDF::AggregationSingle
296
+ load_udf_aggregation_actually(udf)
297
+ end
298
+ end
299
+
285
300
  # this method should be protected with @mutex lock
286
301
  def register_query_actually(query, mapping)
287
302
  # 'mapping' argument is {target => fieldset}
@@ -347,5 +362,32 @@ module Norikra
347
362
  debug "remove event type", :target => target, :event_type => event_type_name
348
363
  @config.removeEventType(event_type_name, true)
349
364
  end
365
+
366
+ VALUE_CACHE_ENUM = com.espertech.esper.client.ConfigurationPlugInSingleRowFunction::ValueCache
367
+ FILTER_OPTIMIZABLE_ENUM = com.espertech.esper.client.ConfigurationPlugInSingleRowFunction::FilterOptimizable
368
+
369
+ def load_udf_actually(udf)
370
+ debug "importing class into config object", :name => udf.class.to_s
371
+
372
+ functionName, className, methodName = udf.definition
373
+
374
+ valueCache = udf.value_cache ? VALUE_CACHE_ENUM::ENABLED : VALUE_CACHE_ENUM::DISABLED
375
+ filterOptimizable = udf.filter_optimizable ? FILTER_OPTIMIZABLE_ENUM::ENABLED : FILTER_OPTIMIZABLE_ENUM::DISABLED
376
+ rethrowExceptions = udf.rethrow_exceptions
377
+
378
+ debug "adding SingleRowFunction", :function => functionName, :javaClass => className, :javaMethod => methodName
379
+ @config.addPlugInSingleRowFunction(functionName, className, methodName, valueCache, filterOptimizable, rethrowExceptions)
380
+ functionName
381
+ end
382
+
383
+ def load_udf_aggregation_actually(udf)
384
+ debug "importing class into config object", :name => udf.class.to_s
385
+
386
+ functionName, factoryClassName = udf.definition
387
+
388
+ debug "adding AggregationSingleFactory", :function => functionName, :javaClass => factoryClassName
389
+ @config.addPlugInAggregationFunctionFactory(functionName, factoryClassName)
390
+ functionName
391
+ end
350
392
  end
351
393
  end
@@ -0,0 +1,49 @@
1
+ # this is note for future update
2
+
3
+ module Norikra
4
+ module UDF
5
+ class FailedUDFImplementationPureRuby
6
+ # require 'jruby/core_ext'
7
+ class WootheeIsCrawler < Norikra::UDF::Base # Norikra::UDF::WootheeIsCrawler < Norikra::UDF::Base
8
+ def self.init
9
+ require 'woothee'
10
+ end
11
+
12
+ def self.function_name
13
+ "isCrawler"
14
+ end
15
+
16
+ def self.isCrawler(agent)
17
+ Woothee.is_crawler(agent)
18
+ end
19
+ class << self
20
+ add_method_signature( "isCrawler", [java.lang.Boolean, java.lang.String] )
21
+ end
22
+ end
23
+
24
+ # for engine.rb
25
+ def load_udf_actually(udf_klass)
26
+ require 'jruby/core_ext'
27
+ udf_klass.init
28
+
29
+ jclass = udf_klass.become_java!(".")
30
+ className = jclass.get_name.to_java(:string)
31
+
32
+ #### try for NullPointerException, but doesn't work well
33
+ # field = jclass.getDeclaredField("ruby");
34
+ # field.setAccessible(java.lang.Boolean::TRUE)
35
+ # field.set(nil, org.jruby.Ruby.getGlobalRuntime)
36
+
37
+ functionName = udf_klass.function_name.to_java(:string)
38
+ methodName = udf_klass.method_name.to_java(:string)
39
+
40
+ valueCache = udf_klass.value_cache ? VALUE_CACHE_ENUM::ENABLED : VALUE_CACHE_ENUM::DISABLED
41
+ filterOptimizable = udf_klass.filter_optimizable ? FILTER_OPTIMIZABLE_ENUM::ENABLED : FILTER_OPTIMIZABLE_ENUM::DISABLED
42
+ rethrowExceptions = udf_klass.rethrow_exceptions
43
+
44
+ debug "adding SingleRowFunction", :class => udf_klass.to_s, :javaClass => jclass.get_name
45
+ @config.addPlugInSingleRowFunction(functionName, className, methodName, valueCache, filterOptimizable, rethrowExceptions)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -9,6 +9,7 @@ require 'norikra/typedef'
9
9
  require 'norikra/query'
10
10
 
11
11
  require 'norikra/rpc'
12
+ require 'norikra/udf'
12
13
 
13
14
  module Norikra
14
15
  class Server
@@ -32,8 +33,11 @@ module Norikra
32
33
  def run
33
34
  @engine.start
34
35
  @rpcserver.start
35
- info "Norikra server started."
36
+
37
+ load_plugins
38
+
36
39
  @running = true
40
+ info "Norikra server started."
37
41
 
38
42
  shutdown_proc = ->{ @running = false }
39
43
  # JVM uses SIGQUIT for thread/heap state dumping
@@ -47,6 +51,22 @@ module Norikra
47
51
  end
48
52
  end
49
53
 
54
+ def load_plugins
55
+ info "Loading UDF plugins"
56
+ Norikra::UDF.listup.each do |mojule|
57
+ if mojule.is_a?(Class)
58
+ name = @engine.load(mojule)
59
+ info "UDF loaded", :name => name
60
+ elsif mojule.is_a?(Module) && mojule.respond_to?(:plugins)
61
+ mojule.init if mojule.respond_to?(:init)
62
+ mojule.plugins.each do |klass|
63
+ name = @engine.load(klass)
64
+ info "UDF loaded", :name => name
65
+ end
66
+ end
67
+ end
68
+ end
69
+
50
70
  def shutdown
51
71
  info "Norikra server shutting down."
52
72
  @rpcserver.stop
@@ -0,0 +1,124 @@
1
+ require 'norikra/error'
2
+ require 'rubygems'
3
+
4
+ module Norikra
5
+ module UDF
6
+ #### esper-4.9.0/esper/doc/reference/html/extension.html#custom-singlerow-function
7
+ # <esper-configuration
8
+ # <plugin-singlerow-function name="getDate"
9
+ # function-class="mycompany.DateUtil" function-method="parseDate"
10
+ # value-cache="enabled"
11
+ # filter-optimizable="disabled"
12
+ # rethrow-exceptions="disabled" />
13
+ # </esper-configuration>
14
+ class SingleRow
15
+ def self.init
16
+ true
17
+ end
18
+
19
+ def definition
20
+ [self.function_name, self.class_name, self.method_name]
21
+ end
22
+
23
+ # UDF function name in queries
24
+ def function_name
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def class_name
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def method_name
33
+ function_name
34
+ end
35
+
36
+ # 17.3.3. Value Cache
37
+ # When a single-row function receives parameters that are all constant values or expressions
38
+ # that themselves receive only constant values, Esper can pre-evaluate the result of
39
+ # the single-row function at time of statement creation.
40
+ # By default, Esper does not pre-evaluate the single-row function unless you configure
41
+ # the value cache as enabled.
42
+ def value_cache
43
+ false
44
+ end
45
+
46
+ # 17.3.4. Single-Row Functions in Filter Predicate Expressions
47
+ # Your EPL may use plug-in single row functions among the predicate expressions
48
+ # as part of the filters in a stream or pattern.
49
+ # For example, the EPL below uses the function computeHash as part of a predicate expression:
50
+ #
51
+ # select * from MyEvent(computeHash(field) = 100)
52
+ #
53
+ # When you have many EPL statements or many context partitions that refer to the same function,
54
+ # event type and parameters in a predicate expression, the engine may optimize evaluation:
55
+ # The function gets evaluated only once per event.
56
+ #
57
+ # While the optimization is enabled by default for all plug-in single row functions,
58
+ # you can also disable the optimization for a specific single-row function.
59
+ # By disabling the optimization for a single-row function the engine may use less memory
60
+ # to identify reusable function footprints but may cause the engine to evaluate each function
61
+ # more frequently then necessary.
62
+ def filter_optimizable
63
+ true
64
+ end
65
+
66
+ # 17.3.7. Exception Handling
67
+ # By default the engine logs any exceptions thrown by the single row function and returns
68
+ # a null value. To have exceptions be re-thrown instead, which makes exceptions visible
69
+ # to any registered exception handler, please configure as discussed herein.
70
+ def rethrow_exceptions
71
+ false
72
+ end
73
+ end
74
+
75
+ class AggregationSingle
76
+ def self.init
77
+ true
78
+ end
79
+
80
+ def definition
81
+ [self.function_name, self.factory_class_name]
82
+ end
83
+
84
+ # UDF aggregation function name in queries
85
+ def function_name
86
+ raise NotImplementedError
87
+ end
88
+
89
+ def factory_class_name
90
+ raise NotImplementedError
91
+ end
92
+ end
93
+
94
+ def self.listup
95
+ return unless defined? Gem
96
+
97
+ #TODO: CHECK: same name files of different versions?
98
+ plugins = Gem.find_files('norikra/udf/*.rb')
99
+ plugins.each do |plugin|
100
+ begin
101
+ debug "plugin file found!", :file => plugin
102
+ load plugin
103
+ rescue => e
104
+ warn "Failed to load norikra UDF plugin", :plugin => plugin.to_s, :error_class => e.class, :error => e.message
105
+ end
106
+ end
107
+
108
+ known_consts = [:SingleRow, :AggregateSingle]
109
+ udf_bases = [Norikra::UDF::SingleRow, Norikra::UDF::AggregationSingle]
110
+ udfs = []
111
+ self.constants.each do |c|
112
+ next if known_consts.include?(c)
113
+
114
+ klass = Norikra::UDF.const_get(c)
115
+ if klass.is_a?(Class) && udf_bases.include?(klass.superclass)
116
+ udfs.push(klass)
117
+ elsif klass.is_a?(Module) && klass.respond_to?(:plugins)
118
+ udfs.push(klass)
119
+ end
120
+ end
121
+ udfs
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,113 @@
1
+ module Norikra
2
+ module UDFSpecHelper
3
+ @@plugins = {}
4
+
5
+ def function(name)
6
+ @@plugins[name.to_s]
7
+ end
8
+
9
+ def fcall(name, *args)
10
+ @@plugins[name.to_s]._call(*args)
11
+ end
12
+
13
+ # params: for AggregationSingle only
14
+ # required keys: :valueType, :windowed, :distinct, :parameters
15
+ # :parameters => [[parameterType, constant?, contantValue], ... ]
16
+ def udf_function(mojule, params={})
17
+ require 'esper-4.9.0.jar'
18
+
19
+ unless mojule.is_a?(Class)
20
+ mojule.init if mojule.respond_to?(:init)
21
+ ps = []
22
+ mojule.plugins.each{|p| ps.push(udf_function(p))} if mojule.respond_to?(:plugins)
23
+ return ps
24
+ end
25
+
26
+ klass = mojule
27
+ klass.init if klass.respond_to?(:init)
28
+
29
+ if klass.superclass == Norikra::UDF::SingleRow
30
+ name, classname, methodname = klass.new.definition
31
+ @@plugins[name] = UDFInstance.new(classname, methodname)
32
+
33
+ elsif klass.superclass == Norikra::UDF::AggregationSingle
34
+ name, factoryclassname = klass.new.definition
35
+ factory = UDFAggregationFactoryInstance.new(factoryclassname)
36
+ factory.check(name, params[:valueType], params[:parameters], params.fetch(:distinct, false), params.fetch(:windowed, true))
37
+ @@plugins[name] = factory.create
38
+ end
39
+ end
40
+
41
+ module UDFHelper
42
+ def classObject(classname)
43
+ parts = classname.split('.')
44
+ clazzname = parts.pop
45
+ eval("Java::" + parts.map(&:capitalize).join + "::" + clazzname)
46
+ end
47
+ end
48
+
49
+ class UDFInstance
50
+ include UDFHelper
51
+
52
+ def initialize(classname, methodname)
53
+ @methodname = methodname
54
+ @clazz = classObject(classname)
55
+ end
56
+
57
+ def _call(*args)
58
+ @clazz.send(@methodname.to_sym, *args)
59
+ end
60
+ end
61
+
62
+ class UDFAggregationInstance
63
+ def initialize(instance)
64
+ @func = instance
65
+ end
66
+
67
+ def _call(type, *args)
68
+ self.send(type, *args)
69
+ end
70
+
71
+ def getValueType; @func.getValueType; end # public Class getValueType()
72
+ def enter(*args); @func.enter(*args); end # public void enter(Object value)
73
+ def leave(*args); @func.leave(*args); end # public void leave(Object value)
74
+ def getValue; @func.getValue; end # public Object getValue()
75
+ def clear; @func.clear; end # public void clear()
76
+ end
77
+
78
+ class UDFAggregationFactoryInstance
79
+ include UDFHelper
80
+
81
+ def initialize(classname)
82
+ @factory = classObject(classname).new
83
+ end
84
+
85
+ # parameters => [[parameterType, constant?, contantValue], ... ]
86
+ def check(func_name, value_type, parameters, distinct, windowed)
87
+ @factory.setFunctionName(func_name)
88
+ unless @factory.getValueType == value_type.java_class
89
+ raise "Aggregation UDF value type mismatch, expected '#{value_type}', actually '#{@factory.getValueType}'"
90
+ end
91
+
92
+ parameterTypes = parameters.map{|t,bc,c| t.java_class }
93
+ constantValue = parameters.map{|t,bc,c| bc.nil? ? false : bc }
94
+ constantValues = parameters.map{|t,bc,c| c }
95
+
96
+ # public AggregationValidationContext(java.lang.Class[] parameterTypes,
97
+ # boolean[] constantValue,
98
+ # java.lang.Object[] constantValues,
99
+ # boolean distinct,
100
+ # boolean windowed,
101
+ # ExprNode[] expressions)
102
+ context_class = com.espertech.esper.epl.agg.service.AggregationValidationContext
103
+ context = context_class.new(parameterTypes, constantValue, constantValues, distinct, windowed, [])
104
+ @factory.validate(context)
105
+ true
106
+ end
107
+
108
+ def create
109
+ UDFAggregationInstance.new(@factory.newAggregator)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -1,3 +1,3 @@
1
1
  module Norikra
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: norikra
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.7
5
+ version: 0.0.8
6
6
  platform: java
7
7
  authors:
8
8
  - TAGOMORI Satoshi
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-07 00:00:00.000000000 Z
12
+ date: 2013-08-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mizuno
@@ -272,9 +272,12 @@ files:
272
272
  - lib/norikra/rpc/error.rb
273
273
  - lib/norikra/rpc/handler.rb
274
274
  - lib/norikra/rpc/http.rb
275
+ - lib/norikra/rubyudf.rb
275
276
  - lib/norikra/server.rb
276
277
  - lib/norikra/typedef.rb
277
278
  - lib/norikra/typedef_manager.rb
279
+ - lib/norikra/udf.rb
280
+ - lib/norikra/udf_spec_helper.rb
278
281
  - lib/norikra/version.rb
279
282
  - norikra.gemspec
280
283
  - script/spec_server_pry