hermann 0.18.1-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 +7 -0
- data/Rakefile +45 -0
- data/ext/hermann/extconf.rb +149 -0
- data/ext/hermann/hermann_lib.c +1041 -0
- data/ext/hermann/hermann_lib.h +107 -0
- data/ext/patches/librdkafka/0006-Update-some-headers-to-include-the-right-headers-to-.patch +57 -0
- data/lib/hermann/consumer.rb +24 -0
- data/lib/hermann/discovery/zookeeper.rb +85 -0
- data/lib/hermann/errors.rb +14 -0
- data/lib/hermann/producer.rb +150 -0
- data/lib/hermann/provider/java_producer.rb +106 -0
- data/lib/hermann/result.rb +74 -0
- data/lib/hermann/timeout.rb +37 -0
- data/lib/hermann/version.rb +3 -0
- data/lib/hermann.rb +20 -0
- data/lib/hermann_jars.rb +15 -0
- metadata +137 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
/*
|
2
|
+
* hermann_lib.h - Ruby wrapper for the librdkafka library
|
3
|
+
*
|
4
|
+
* Copyright (c) 2014 Stan Campbell
|
5
|
+
* Copyright (c) 2014 Lookout, Inc.
|
6
|
+
* All rights reserved.
|
7
|
+
*
|
8
|
+
* Redistribution and use in source and binary forms, with or without
|
9
|
+
* modification, are permitted provided that the following conditions are met:
|
10
|
+
*
|
11
|
+
* 1. Redistributions of source code must retain the above copyright notice,
|
12
|
+
* this list of conditions and the following disclaimer.
|
13
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
14
|
+
* this list of conditions and the following disclaimer in the documentation
|
15
|
+
* and/or other materials provided with the distribution.
|
16
|
+
*
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
21
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
22
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
23
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
25
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
*/
|
29
|
+
|
30
|
+
#ifndef HERMANN_H
|
31
|
+
#define HERMANN_H
|
32
|
+
|
33
|
+
#include <ruby.h>
|
34
|
+
|
35
|
+
#include <ctype.h>
|
36
|
+
#include <signal.h>
|
37
|
+
#include <string.h>
|
38
|
+
#include <unistd.h>
|
39
|
+
#include <stdlib.h>
|
40
|
+
#include <syslog.h>
|
41
|
+
#include <sys/time.h>
|
42
|
+
#include <errno.h>
|
43
|
+
|
44
|
+
#include <librdkafka/rdkafka.h>
|
45
|
+
|
46
|
+
#ifdef TRACE
|
47
|
+
#define TRACER(...) do { \
|
48
|
+
fprintf(stderr, "%i:%s()> ", __LINE__, __PRETTY_FUNCTION__); \
|
49
|
+
fprintf(stderr, __VA_ARGS__); \
|
50
|
+
fflush(stderr); \
|
51
|
+
} while (0)
|
52
|
+
#else
|
53
|
+
#define TRACER(...) do { } while (0)
|
54
|
+
#endif
|
55
|
+
|
56
|
+
// Holds the defined Ruby module for Hermann
|
57
|
+
static VALUE hermann_module;
|
58
|
+
|
59
|
+
#define HERMANN_MAX_ERRSTR_LEN 512
|
60
|
+
|
61
|
+
static int DEBUG = 0;
|
62
|
+
|
63
|
+
// Should we expect rb_thread_blocking_region to be present?
|
64
|
+
// #define RB_THREAD_BLOCKING_REGION
|
65
|
+
#undef RB_THREAD_BLOCKING_REGION
|
66
|
+
|
67
|
+
static enum {
|
68
|
+
OUTPUT_HEXDUMP,
|
69
|
+
OUTPUT_RAW,
|
70
|
+
} output = OUTPUT_HEXDUMP;
|
71
|
+
|
72
|
+
typedef struct HermannInstanceConfig {
|
73
|
+
char *topic;
|
74
|
+
|
75
|
+
/* Kafka configuration */
|
76
|
+
rd_kafka_t *rk;
|
77
|
+
rd_kafka_topic_t *rkt;
|
78
|
+
char *brokers;
|
79
|
+
int partition;
|
80
|
+
rd_kafka_topic_conf_t *topic_conf;
|
81
|
+
char errstr[512];
|
82
|
+
rd_kafka_conf_t *conf;
|
83
|
+
const char *debug;
|
84
|
+
int64_t start_offset;
|
85
|
+
int do_conf_dump;
|
86
|
+
|
87
|
+
int run;
|
88
|
+
int exit_eof;
|
89
|
+
int quiet;
|
90
|
+
|
91
|
+
int isInitialized;
|
92
|
+
int isConnected;
|
93
|
+
|
94
|
+
int isErrored;
|
95
|
+
char *error;
|
96
|
+
} HermannInstanceConfig;
|
97
|
+
|
98
|
+
typedef HermannInstanceConfig hermann_conf_t;
|
99
|
+
|
100
|
+
typedef struct {
|
101
|
+
/* Hermann::Lib::Producer */
|
102
|
+
hermann_conf_t *producer;
|
103
|
+
/* Hermann::Result */
|
104
|
+
VALUE result;
|
105
|
+
} hermann_push_ctx_t;
|
106
|
+
|
107
|
+
#endif
|
@@ -0,0 +1,57 @@
|
|
1
|
+
From 888ca33b571d99e877d665235b822f7c961c8fdb Mon Sep 17 00:00:00 2001
|
2
|
+
From: "R. Tyler Croy" <tyler@monkeypox.org>
|
3
|
+
Date: Thu, 28 Aug 2014 16:24:04 -0700
|
4
|
+
Subject: [PATCH 6/8] Update some headers to include the right headers to build
|
5
|
+
on FreeBSD
|
6
|
+
|
7
|
+
---
|
8
|
+
src/rd.h | 9 +++++++++
|
9
|
+
src/rdaddr.h | 4 ++++
|
10
|
+
2 files changed, 13 insertions(+)
|
11
|
+
|
12
|
+
diff --git a/src/rd.h b/src/rd.h
|
13
|
+
index c31501e..4789493 100644
|
14
|
+
--- a/src/rd.h
|
15
|
+
+++ b/src/rd.h
|
16
|
+
@@ -37,7 +37,11 @@
|
17
|
+
#include <errno.h>
|
18
|
+
#include <time.h>
|
19
|
+
#include <sys/time.h>
|
20
|
+
+
|
21
|
+
+#ifndef __FreeBSD__
|
22
|
+
+/* alloca(3) is in stdlib on FreeBSD */
|
23
|
+
#include <alloca.h>
|
24
|
+
+#endif
|
25
|
+
#include <assert.h>
|
26
|
+
#include <pthread.h>
|
27
|
+
|
28
|
+
@@ -110,6 +114,11 @@
|
29
|
+
# endif
|
30
|
+
#endif /* sun */
|
31
|
+
|
32
|
+
+#ifdef __FreeBSD__
|
33
|
+
+/* FreeBSD defines be64toh() in sys/endian.h */
|
34
|
+
+#include <sys/endian.h>
|
35
|
+
+#endif
|
36
|
+
+
|
37
|
+
#ifndef be64toh
|
38
|
+
#ifndef __APPLE__
|
39
|
+
#ifndef sun
|
40
|
+
diff --git a/src/rdaddr.h b/src/rdaddr.h
|
41
|
+
index 0b37354..e55bd55 100644
|
42
|
+
--- a/src/rdaddr.h
|
43
|
+
+++ b/src/rdaddr.h
|
44
|
+
@@ -32,6 +32,10 @@
|
45
|
+
#include <arpa/inet.h>
|
46
|
+
#include <netdb.h>
|
47
|
+
|
48
|
+
+#ifdef __FreeBSD__
|
49
|
+
+#include <sys/socket.h>
|
50
|
+
+#endif
|
51
|
+
+
|
52
|
+
/**
|
53
|
+
* rd_sockaddr_inx_t is a union for either ipv4 or ipv6 sockaddrs.
|
54
|
+
* It provides conveniant abstraction of AF_INET* agnostic operations.
|
55
|
+
--
|
56
|
+
1.9.0
|
57
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'hermann'
|
2
|
+
|
3
|
+
unless Hermann.jruby?
|
4
|
+
require 'hermann_lib'
|
5
|
+
end
|
6
|
+
|
7
|
+
module Hermann
|
8
|
+
class Consumer
|
9
|
+
attr_reader :topic, :brokers, :partition, :internal
|
10
|
+
|
11
|
+
def initialize(topic, brokers, partition)
|
12
|
+
@topic = topic
|
13
|
+
@brokers = brokers
|
14
|
+
@partition = partition
|
15
|
+
unless Hermann.jruby?
|
16
|
+
@internal = Hermann::Lib::Consumer.new(topic, brokers, partition)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def consume(&block)
|
21
|
+
@internal.consume(&block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'hermann'
|
2
|
+
require 'zk'
|
3
|
+
require 'json'
|
4
|
+
require 'hermann/errors'
|
5
|
+
|
6
|
+
module Hermann
|
7
|
+
module Discovery
|
8
|
+
|
9
|
+
|
10
|
+
# Communicates with Zookeeper to discover kafka broker ids
|
11
|
+
#
|
12
|
+
class Zookeeper
|
13
|
+
attr_reader :zookeepers
|
14
|
+
|
15
|
+
BROKERS_PATH = "/brokers/ids".freeze
|
16
|
+
|
17
|
+
def initialize(zookeepers)
|
18
|
+
@zookeepers = zookeepers
|
19
|
+
end
|
20
|
+
|
21
|
+
# Gets comma separated string of brokers
|
22
|
+
#
|
23
|
+
# @param [Fixnum] timeout to connect to zookeeper, "2 times the
|
24
|
+
# tickTime (as set in the server configuration) and a maximum
|
25
|
+
# of 20 times the tickTime2 times the tick time set on server"
|
26
|
+
#
|
27
|
+
# @return [String] comma separated list of brokers
|
28
|
+
#
|
29
|
+
# @raises [NoBrokersError] if could not discover brokers thru zookeeper
|
30
|
+
def get_brokers(timeout=0)
|
31
|
+
brokers = []
|
32
|
+
ZK.open(zookeepers, {:timeout => timeout}) do |zk|
|
33
|
+
brokers = fetch_brokers(zk)
|
34
|
+
end
|
35
|
+
if brokers.empty?
|
36
|
+
raise Hermann::Errors::NoBrokersError
|
37
|
+
end
|
38
|
+
brokers.join(',')
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Gets an Array of broker strings
|
44
|
+
#
|
45
|
+
# @param [ZK::Client] zookeeper client
|
46
|
+
#
|
47
|
+
# @return array of broker strings
|
48
|
+
def fetch_brokers(zk)
|
49
|
+
brokers = []
|
50
|
+
zk.children(BROKERS_PATH).each do |id|
|
51
|
+
node = fetch_znode(zk, id)
|
52
|
+
next if node.nil? # whatever error could happen from ZK#get
|
53
|
+
brokers << format_broker_from_znode(node)
|
54
|
+
end
|
55
|
+
brokers.compact
|
56
|
+
end
|
57
|
+
|
58
|
+
# Gets node from zookeeper
|
59
|
+
#
|
60
|
+
# @param [ZK::Client] zookeeper client
|
61
|
+
# @param [Fixnum] kafka broker
|
62
|
+
#
|
63
|
+
# @return [String] node data
|
64
|
+
def fetch_znode(zk, id)
|
65
|
+
zk.get("#{BROKERS_PATH}/#{id}")[0]
|
66
|
+
rescue ZK::Exceptions::NoNode
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
# Formats the node data into string
|
71
|
+
#
|
72
|
+
# @param [String] node data
|
73
|
+
#
|
74
|
+
# @return [String] formatted node data or empty string if error
|
75
|
+
def format_broker_from_znode(znode)
|
76
|
+
hash = JSON.parse(znode)
|
77
|
+
host = hash['host']
|
78
|
+
port = hash['port']
|
79
|
+
host && port ? "#{host}:#{port}" : nil
|
80
|
+
rescue JSON::ParserError
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
module Hermann
|
3
|
+
module Errors
|
4
|
+
# Error for connectivity problems with the Kafka brokers
|
5
|
+
class ConnectivityError; end;
|
6
|
+
|
7
|
+
# For passing incorrect config and options to kafka
|
8
|
+
class ConfigurationError < StandardError; end
|
9
|
+
|
10
|
+
# cannot discover brokers from zookeeper
|
11
|
+
class NoBrokersError < StandardError; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'hermann'
|
2
|
+
require 'hermann/result'
|
3
|
+
|
4
|
+
|
5
|
+
if RUBY_PLATFORM == "java"
|
6
|
+
require 'hermann/provider/java_producer'
|
7
|
+
else
|
8
|
+
require 'hermann_lib'
|
9
|
+
end
|
10
|
+
|
11
|
+
module Hermann
|
12
|
+
class Producer
|
13
|
+
attr_reader :topic, :brokers, :internal, :children
|
14
|
+
|
15
|
+
# Initialize a producer object with a default topic and broker list
|
16
|
+
#
|
17
|
+
# @param [String] topic The default topic to use for pushing messages
|
18
|
+
# @param [Array] brokers An array of "host:port" strings for the brokers
|
19
|
+
def initialize(topic, brokers, opts={})
|
20
|
+
@topic = topic
|
21
|
+
@brokers = brokers
|
22
|
+
if RUBY_PLATFORM == "java"
|
23
|
+
@internal = Hermann::Provider::JavaProducer.new(brokers, opts)
|
24
|
+
else
|
25
|
+
@internal = Hermann::Lib::Producer.new(brokers)
|
26
|
+
end
|
27
|
+
# We're tracking children so we can make sure that at Producer exit we
|
28
|
+
# make a reasonable attempt to clean up outstanding result objects
|
29
|
+
@children = []
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Boolean] True if our underlying producer object thinks it's
|
33
|
+
# connected to a Kafka broker
|
34
|
+
def connected?
|
35
|
+
return @internal.connected?
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Boolean] True if the underlying producer object has errored
|
39
|
+
def errored?
|
40
|
+
return @internal.errored?
|
41
|
+
end
|
42
|
+
|
43
|
+
def connect(timeout=0)
|
44
|
+
return @internal.connect(timeout * 1000)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Push a value onto the Kafka topic passed to this +Producer+
|
48
|
+
#
|
49
|
+
# @param [Object] value A single object to push
|
50
|
+
# @param [Hash] opts to pass to push method
|
51
|
+
# @option opts [String] :topic The topic to push messages to
|
52
|
+
#
|
53
|
+
# @return [Hermann::Result] A future-like object which will store the
|
54
|
+
# result from the broker
|
55
|
+
def push(value, opts={})
|
56
|
+
topic = opts[:topic] || @topic
|
57
|
+
result = nil
|
58
|
+
|
59
|
+
if value.kind_of? Array
|
60
|
+
return value.map { |e| self.push(e, opts) }
|
61
|
+
end
|
62
|
+
|
63
|
+
if RUBY_PLATFORM == "java"
|
64
|
+
result = @internal.push_single(value, topic, nil)
|
65
|
+
unless result.nil?
|
66
|
+
@children << result
|
67
|
+
end
|
68
|
+
# Reaping children on the push just to make sure that it does get
|
69
|
+
# called correctly and we don't leak memory
|
70
|
+
reap_children
|
71
|
+
else
|
72
|
+
result = create_result
|
73
|
+
@internal.push_single(value, topic, result)
|
74
|
+
end
|
75
|
+
|
76
|
+
return result
|
77
|
+
end
|
78
|
+
|
79
|
+
# Create a +Hermann::Result+ that is tracked in the Producer's children
|
80
|
+
# array
|
81
|
+
#
|
82
|
+
# @return [Hermann::Result] A new, unused, result
|
83
|
+
def create_result
|
84
|
+
@children << Hermann::Result.new(self)
|
85
|
+
return @children.last
|
86
|
+
end
|
87
|
+
|
88
|
+
# Tick the underlying librdkafka reacter and clean up any unreaped but
|
89
|
+
# reapable children results
|
90
|
+
#
|
91
|
+
# @param [FixNum] timeout Seconds to block on the internal reactor
|
92
|
+
# @return [FixNum] Number of +Hermann::Result+ children reaped
|
93
|
+
def tick_reactor(timeout=0)
|
94
|
+
begin
|
95
|
+
execute_tick(rounded_timeout(timeout))
|
96
|
+
rescue StandardError => ex
|
97
|
+
@children.each do |child|
|
98
|
+
# Skip over any children that should already be reaped for other
|
99
|
+
# reasons
|
100
|
+
next if child.completed?
|
101
|
+
# Propagate errors to the remaining children
|
102
|
+
child.internal_set_error(ex)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Reaping the children at this point will also reap any children marked
|
107
|
+
# as errored by an exception out of #execute_tick
|
108
|
+
return reap_children
|
109
|
+
end
|
110
|
+
|
111
|
+
# @return [FixNum] number of children reaped
|
112
|
+
def reap_children
|
113
|
+
# Filter all children who are no longer pending/fulfilled
|
114
|
+
total_children = @children.size
|
115
|
+
|
116
|
+
@children = @children.reject { |c| c.completed? }
|
117
|
+
|
118
|
+
return (total_children - children.size)
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def rounded_timeout(timeout)
|
125
|
+
# Handle negative numbers, those can be zero
|
126
|
+
return 0 if (timeout < 0)
|
127
|
+
# Since we're going to sleep for each second, round any potential floats
|
128
|
+
# off
|
129
|
+
return timeout.round if timeout.kind_of?(Float)
|
130
|
+
return timeout
|
131
|
+
end
|
132
|
+
|
133
|
+
# Perform the actual reactor tick
|
134
|
+
# @raises [StandardError] in case of underlying failures in librdkafka
|
135
|
+
def execute_tick(timeout)
|
136
|
+
if timeout == 0
|
137
|
+
@internal.tick(0)
|
138
|
+
else
|
139
|
+
(timeout * 2).times do
|
140
|
+
# We're going to Thread#sleep in Ruby to avoid a
|
141
|
+
# pthread_cond_timedwait(3) inside of librdkafka
|
142
|
+
events = @internal.tick(0)
|
143
|
+
# If we find events, break out early
|
144
|
+
break if events > 0
|
145
|
+
sleep 0.5
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'hermann'
|
2
|
+
require 'concurrent'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Hermann
|
6
|
+
module Provider
|
7
|
+
# This class simulates the kafka producer class within a java environment.
|
8
|
+
# If the producer throw an exception within the Promise a call to +.value!+
|
9
|
+
# will raise the exception and the rejected flag will be set to true
|
10
|
+
#
|
11
|
+
class JavaProducer
|
12
|
+
attr_accessor :producer
|
13
|
+
|
14
|
+
|
15
|
+
# Instantiate JavaProducer
|
16
|
+
#
|
17
|
+
# @params [String] list of brokers
|
18
|
+
#
|
19
|
+
# @params [Hash] hash of kafka attributes, overrides defaults
|
20
|
+
#
|
21
|
+
# @raises [RuntimeError] if brokers string is nil/empty
|
22
|
+
#
|
23
|
+
# ==== Examples
|
24
|
+
#
|
25
|
+
# JavaProducer.new('0:9092', {'request.required.acks' => '1'})
|
26
|
+
#
|
27
|
+
def initialize(brokers, opts={})
|
28
|
+
properties = create_properties(brokers, opts)
|
29
|
+
config = create_config(properties)
|
30
|
+
@producer = JavaApiUtil::Producer.new(config)
|
31
|
+
end
|
32
|
+
|
33
|
+
DEFAULTS = {
|
34
|
+
'serializer.class' => 'kafka.serializer.StringEncoder',
|
35
|
+
'partitioner.class' => 'kafka.producer.DefaultPartitioner',
|
36
|
+
'request.required.acks' => '1'
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
# Push a value onto the Kafka topic passed to this +Producer+
|
40
|
+
#
|
41
|
+
# @param [Object] value A single object to push
|
42
|
+
# @param [String] topic to push message to
|
43
|
+
#
|
44
|
+
# @return +Concurrent::Promise+ Representa a promise to send the
|
45
|
+
# data to the kafka broker. Upon execution the Promise's status
|
46
|
+
# will be set
|
47
|
+
def push_single(msg, topic, unused)
|
48
|
+
Concurrent::Promise.execute {
|
49
|
+
data = ProducerUtil::KeyedMessage.new(topic, msg)
|
50
|
+
@producer.send(data)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# No-op for now
|
55
|
+
def connected?
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
# No-op for now
|
60
|
+
def errored?
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
|
64
|
+
# No-op for now
|
65
|
+
def connect(timeout=0)
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Creates a ProducerConfig object
|
72
|
+
#
|
73
|
+
# @param [Properties] object with broker properties
|
74
|
+
#
|
75
|
+
# @return [ProducerConfig] - packaged config for +Producer+
|
76
|
+
def create_config(properties)
|
77
|
+
ProducerUtil::ProducerConfig.new(properties)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates Properties Object
|
81
|
+
#
|
82
|
+
# @param [Hash] brokers passed into this function
|
83
|
+
# @option args [String] :brokers - string of brokers
|
84
|
+
#
|
85
|
+
# @return [Properties] properties object for creating +ProducerConfig+
|
86
|
+
#
|
87
|
+
# @raises [RuntimeError] if options does not contain key value strings
|
88
|
+
def create_properties(brokers, opts={})
|
89
|
+
brokers = { 'metadata.broker.list' => brokers }
|
90
|
+
options = DEFAULTS.merge(brokers).merge(opts)
|
91
|
+
properties = JavaUtil::Properties.new
|
92
|
+
options.each do |key, val|
|
93
|
+
validate_property!(key, val)
|
94
|
+
properties.put(key, val)
|
95
|
+
end
|
96
|
+
properties
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate_property!(key, val)
|
100
|
+
if key.to_s.empty? || val.to_s.empty?
|
101
|
+
raise Hermann::Errors::ConfigurationError, "Invalid Broker Properties"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module Hermann
|
3
|
+
class Result
|
4
|
+
attr_reader :reason, :state
|
5
|
+
|
6
|
+
STATES = [:pending,
|
7
|
+
:rejected,
|
8
|
+
:fulfilled,
|
9
|
+
:unfulfilled,
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
def initialize(producer)
|
13
|
+
@producer = producer
|
14
|
+
@reason = nil
|
15
|
+
@value = nil
|
16
|
+
@state = :unfulfilled
|
17
|
+
end
|
18
|
+
|
19
|
+
STATES.each do |state|
|
20
|
+
define_method("#{state}?".to_sym) do
|
21
|
+
return @state == state
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean] True if this child can be reaped
|
26
|
+
def completed?
|
27
|
+
return true if rejected? || fulfilled?
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Access the value of the future
|
32
|
+
#
|
33
|
+
# @param [FixNum] timeout Seconds to wait on the underlying machinery for a
|
34
|
+
# result
|
35
|
+
# @return [NilClass] nil if no value could be received in the time alotted
|
36
|
+
# @return [Object]
|
37
|
+
def value(timeout=0)
|
38
|
+
@producer.tick_reactor(timeout)
|
39
|
+
return @value
|
40
|
+
end
|
41
|
+
|
42
|
+
# INTERNAL METHOD ONLY. Do not use
|
43
|
+
#
|
44
|
+
# This method will be invoked by the underlying extension to indicate set
|
45
|
+
# the actual value after a callback has completed
|
46
|
+
#
|
47
|
+
# @param [Object] value The actual resulting value
|
48
|
+
# @param [Boolean] is_error True if the result was errored for whatever
|
49
|
+
# reason
|
50
|
+
def internal_set_value(value, is_error)
|
51
|
+
@value = value
|
52
|
+
|
53
|
+
if is_error
|
54
|
+
puts "Hermann::Result#set_internal_value(#{value.class}:\"#{value}\", error?:#{is_error})"
|
55
|
+
@state = :rejected
|
56
|
+
else
|
57
|
+
@state = :fulfilled
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# INTERNAL METHOD ONLY. Do not use
|
62
|
+
#
|
63
|
+
# This method will set our internal #reason with the details from the
|
64
|
+
# exception
|
65
|
+
#
|
66
|
+
# @param [Exception] exception
|
67
|
+
def internal_set_error(exception)
|
68
|
+
return if exception.nil?
|
69
|
+
|
70
|
+
@reason = exception
|
71
|
+
@state = :rejected
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'system_timer'
|
3
|
+
module Hermann
|
4
|
+
class Timeout
|
5
|
+
USE_SYSTEM_TIMER = true
|
6
|
+
end
|
7
|
+
end
|
8
|
+
rescue LoadError
|
9
|
+
require 'timeout'
|
10
|
+
|
11
|
+
if RUBY_VERSION == '1.8.7'
|
12
|
+
puts ">>> You are running on 1.8.7 without SystemTimer"
|
13
|
+
puts ">>> which means Hermann::Timeout will not work as expected"
|
14
|
+
end
|
15
|
+
module Hermann
|
16
|
+
class Timeout
|
17
|
+
USE_SYSTEM_TIMER = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Hermann
|
23
|
+
class Timeout
|
24
|
+
def self.system_timer?
|
25
|
+
Hermann::Timeout::USE_SYSTEM_TIMER
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.timeout(seconds, klass=nil, &block)
|
29
|
+
if system_timer?
|
30
|
+
SystemTimer.timeout_after(seconds, klass, &block)
|
31
|
+
else
|
32
|
+
::Timeout.timeout(seconds, klass, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
data/lib/hermann.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Hermann
|
2
|
+
def self.jruby?
|
3
|
+
return RUBY_PLATFORM == "java"
|
4
|
+
end
|
5
|
+
|
6
|
+
if self.jruby?
|
7
|
+
require 'java'
|
8
|
+
require 'hermann_jars'
|
9
|
+
|
10
|
+
module JavaUtil
|
11
|
+
include_package 'java.util'
|
12
|
+
end
|
13
|
+
module ProducerUtil
|
14
|
+
include_package 'kafka.producer'
|
15
|
+
end
|
16
|
+
module JavaApiUtil
|
17
|
+
include_package 'kafka.javaapi.producer'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/hermann_jars.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# this is a generated file, to avoid over-writing it just delete this comment
|
2
|
+
require 'jar_dependencies'
|
3
|
+
|
4
|
+
require_jar( 'org.slf4j', 'slf4j-api', '1.7.2' )
|
5
|
+
require_jar( 'org.scala-lang', 'scala-library', '2.10.1' )
|
6
|
+
require_jar( 'log4j', 'log4j', '1.2.14' )
|
7
|
+
require_jar( 'com.yammer.metrics', 'metrics-core', '2.2.0' )
|
8
|
+
require_jar( 'org.apache.zookeeper', 'zookeeper', '3.3.4' )
|
9
|
+
require_jar( 'net.sf.jopt-simple', 'jopt-simple', '3.2' )
|
10
|
+
require_jar( 'org.apache.kafka', 'kafka_2.10', '0.8.1.1' )
|
11
|
+
require_jar( 'jline', 'jline', '0.9.94' )
|
12
|
+
require_jar( 'com.101tec', 'zkclient', '0.3' )
|
13
|
+
require_jar( 'org.mod4j.org.eclipse.xtext', 'log4j', '1.2.15' )
|
14
|
+
require_jar( 'junit', 'junit', '3.8.1' )
|
15
|
+
require_jar( 'org.xerial.snappy', 'snappy-java', '1.0.5' )
|