cassandra-driver 1.0.0.rc.1-java → 1.1.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +58 -18
- data/lib/cassandra.rb +132 -93
- data/lib/cassandra/auth.rb +3 -3
- data/lib/cassandra/cluster.rb +65 -39
- data/lib/cassandra/cluster/client.rb +67 -28
- data/lib/cassandra/{client/connection_manager.rb → cluster/connection_pool.rb} +9 -3
- data/lib/cassandra/cluster/connector.rb +101 -30
- data/lib/cassandra/cluster/control_connection.rb +160 -96
- data/lib/cassandra/{client/null_logger.rb → cluster/failed_connection.rb} +12 -14
- data/lib/cassandra/cluster/options.rb +26 -11
- data/lib/cassandra/cluster/schema.rb +22 -1
- data/lib/cassandra/column.rb +5 -0
- data/lib/cassandra/driver.rb +46 -12
- data/lib/cassandra/errors.rb +5 -5
- data/lib/cassandra/execution/options.rb +42 -8
- data/lib/cassandra/execution/trace.rb +4 -4
- data/lib/cassandra/executors.rb +111 -0
- data/lib/cassandra/future.rb +88 -64
- data/lib/cassandra/keyspace.rb +12 -0
- data/lib/cassandra/load_balancing.rb +10 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +10 -5
- data/lib/cassandra/load_balancing/policies/round_robin.rb +7 -5
- data/lib/cassandra/load_balancing/policies/token_aware.rb +31 -10
- data/lib/cassandra/load_balancing/policies/white_list.rb +4 -7
- data/lib/cassandra/null_logger.rb +35 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +8 -1
- data/lib/cassandra/protocol/requests/query_request.rb +1 -11
- data/lib/cassandra/result.rb +34 -9
- data/lib/cassandra/session.rb +6 -0
- data/lib/cassandra/statements/prepared.rb +5 -1
- data/lib/cassandra/table.rb +5 -0
- data/lib/cassandra/util.rb +130 -0
- data/lib/cassandra/version.rb +1 -1
- metadata +40 -50
- data/lib/cassandra/client.rb +0 -144
- data/lib/cassandra/client/batch.rb +0 -212
- data/lib/cassandra/client/client.rb +0 -591
- data/lib/cassandra/client/column_metadata.rb +0 -54
- data/lib/cassandra/client/connector.rb +0 -273
- data/lib/cassandra/client/execute_options_decoder.rb +0 -59
- data/lib/cassandra/client/peer_discovery.rb +0 -50
- data/lib/cassandra/client/prepared_statement.rb +0 -314
- data/lib/cassandra/client/query_result.rb +0 -230
- data/lib/cassandra/client/request_runner.rb +0 -70
- data/lib/cassandra/client/result_metadata.rb +0 -48
- data/lib/cassandra/client/void_result.rb +0 -78
@@ -23,23 +23,38 @@ module Cassandra
|
|
23
23
|
attr_reader :credentials, :auth_provider, :compressor, :port,
|
24
24
|
:connect_timeout, :ssl, :connections_per_local_node,
|
25
25
|
:connections_per_remote_node, :heartbeat_interval,
|
26
|
-
:idle_timeout
|
26
|
+
:idle_timeout, :schema_refresh_delay, :schema_refresh_timeout
|
27
27
|
attr_accessor :protocol_version
|
28
28
|
|
29
|
-
def initialize(protocol_version, credentials, auth_provider, compressor, port, connect_timeout, ssl, connections_per_local_node, connections_per_remote_node, heartbeat_interval, idle_timeout)
|
30
|
-
@protocol_version
|
31
|
-
@credentials
|
32
|
-
@auth_provider
|
33
|
-
@compressor
|
34
|
-
@port
|
35
|
-
@connect_timeout
|
36
|
-
@ssl
|
37
|
-
@heartbeat_interval
|
38
|
-
@idle_timeout
|
29
|
+
def initialize(protocol_version, credentials, auth_provider, compressor, port, connect_timeout, ssl, connections_per_local_node, connections_per_remote_node, heartbeat_interval, idle_timeout, synchronize_schema, schema_refresh_delay, schema_refresh_timeout)
|
30
|
+
@protocol_version = protocol_version
|
31
|
+
@credentials = credentials
|
32
|
+
@auth_provider = auth_provider
|
33
|
+
@compressor = compressor
|
34
|
+
@port = port
|
35
|
+
@connect_timeout = connect_timeout
|
36
|
+
@ssl = ssl
|
37
|
+
@heartbeat_interval = heartbeat_interval
|
38
|
+
@idle_timeout = idle_timeout
|
39
|
+
@synchronize_schema = synchronize_schema
|
40
|
+
@schema_refresh_delay = schema_refresh_delay
|
41
|
+
@schema_refresh_timeout = schema_refresh_timeout
|
39
42
|
|
40
43
|
@connections_per_local_node = connections_per_local_node
|
41
44
|
@connections_per_remote_node = connections_per_remote_node
|
42
45
|
end
|
46
|
+
|
47
|
+
def synchronize_schema?
|
48
|
+
@synchronize_schema
|
49
|
+
end
|
50
|
+
|
51
|
+
def compression
|
52
|
+
@compressor && @compressor.algorithm
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_authenticator(authentication_class)
|
56
|
+
@auth_provider && @auth_provider.create_authenticator(authentication_class)
|
57
|
+
end
|
43
58
|
end
|
44
59
|
end
|
45
60
|
end
|
@@ -130,6 +130,9 @@ module Cassandra
|
|
130
130
|
|
131
131
|
return self unless keyspace
|
132
132
|
|
133
|
+
columns = columns.each_with_object(::Hash.new) do |row, index|
|
134
|
+
index[row['column_name']] = row
|
135
|
+
end
|
133
136
|
table = create_table(table, columns, host.release_version)
|
134
137
|
keyspace = keyspace.update_table(table)
|
135
138
|
|
@@ -139,7 +142,25 @@ module Cassandra
|
|
139
142
|
@keyspaces = keyspaces
|
140
143
|
end
|
141
144
|
|
142
|
-
|
145
|
+
keyspace_changed(keyspace)
|
146
|
+
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
def delete_table(keyspace_name, table_name)
|
151
|
+
keyspace = @keyspaces[keyspace_name]
|
152
|
+
|
153
|
+
return self unless keyspace
|
154
|
+
|
155
|
+
keyspace = keyspace.delete_table(table_name)
|
156
|
+
|
157
|
+
synchronize do
|
158
|
+
keyspaces = @keyspaces.dup
|
159
|
+
keyspaces[keyspace_name] = keyspace
|
160
|
+
@keyspaces = keyspaces
|
161
|
+
end
|
162
|
+
|
163
|
+
keyspace_changed(keyspace)
|
143
164
|
|
144
165
|
self
|
145
166
|
end
|
data/lib/cassandra/column.rb
CHANGED
@@ -78,6 +78,11 @@ module Cassandra
|
|
78
78
|
cql
|
79
79
|
end
|
80
80
|
|
81
|
+
# @return [String] a CLI-friendly column representation
|
82
|
+
def inspect
|
83
|
+
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} @name=#{@name}>"
|
84
|
+
end
|
85
|
+
|
81
86
|
# @return [Boolean] whether this column is equal to the other
|
82
87
|
def eql?(other)
|
83
88
|
other.is_a?(Column) &&
|
data/lib/cassandra/driver.rb
CHANGED
@@ -20,7 +20,7 @@ module Cassandra
|
|
20
20
|
# @private
|
21
21
|
class Driver
|
22
22
|
def self.let(name, &block)
|
23
|
-
define_method(name) { @instances[name]
|
23
|
+
define_method(name) { @instances.has_key?(name) ? @instances[name] : @instances[name] = instance_eval(&block) }
|
24
24
|
define_method(:"#{name}=") { |object| @instances[name] = object }
|
25
25
|
end
|
26
26
|
|
@@ -42,7 +42,9 @@ module Cassandra
|
|
42
42
|
no_replication_strategy
|
43
43
|
)
|
44
44
|
}
|
45
|
-
|
45
|
+
|
46
|
+
let(:executor) { Executors::ThreadPool.new(thread_pool_size) }
|
47
|
+
let(:futures_factory) { Future::Factory.new(executor) }
|
46
48
|
|
47
49
|
let(:schema_type_parser) { Cluster::Schema::TypeParser.new }
|
48
50
|
|
@@ -54,11 +56,11 @@ module Cassandra
|
|
54
56
|
let(:ordered_partitioner) { Cluster::Schema::Partitioners::Ordered.new }
|
55
57
|
let(:random_partitioner) { Cluster::Schema::Partitioners::Random.new }
|
56
58
|
|
57
|
-
let(:connector) { Cluster::Connector.new(logger, io_reactor, cluster_registry, connection_options) }
|
59
|
+
let(:connector) { Cluster::Connector.new(logger, io_reactor, cluster_registry, connection_options, execution_options) }
|
58
60
|
|
59
|
-
let(:control_connection) { Cluster::ControlConnection.new(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, address_resolution_policy, connector) }
|
61
|
+
let(:control_connection) { Cluster::ControlConnection.new(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, address_resolution_policy, connector, connection_options) }
|
60
62
|
|
61
|
-
let(:cluster) { Cluster.new(logger, io_reactor, control_connection, cluster_registry, cluster_schema, cluster_metadata, execution_options, connection_options, load_balancing_policy, reconnection_policy, retry_policy, address_resolution_policy, connector, futures_factory) }
|
63
|
+
let(:cluster) { Cluster.new(logger, io_reactor, executor, control_connection, cluster_registry, cluster_schema, cluster_metadata, execution_options, connection_options, load_balancing_policy, reconnection_policy, retry_policy, address_resolution_policy, connector, futures_factory) }
|
62
64
|
|
63
65
|
let(:execution_options) do
|
64
66
|
Execution::Options.new({
|
@@ -69,27 +71,49 @@ module Cassandra
|
|
69
71
|
})
|
70
72
|
end
|
71
73
|
|
72
|
-
let(:connection_options)
|
74
|
+
let(:connection_options) do
|
75
|
+
Cluster::Options.new(
|
76
|
+
protocol_version,
|
77
|
+
credentials,
|
78
|
+
auth_provider,
|
79
|
+
compressor,
|
80
|
+
port,
|
81
|
+
connect_timeout,
|
82
|
+
ssl,
|
83
|
+
connections_per_local_node,
|
84
|
+
connections_per_remote_node,
|
85
|
+
heartbeat_interval,
|
86
|
+
idle_timeout,
|
87
|
+
synchronize_schema,
|
88
|
+
schema_refresh_delay,
|
89
|
+
schema_refresh_timeout
|
90
|
+
)
|
91
|
+
end
|
73
92
|
|
74
93
|
let(:port) { 9042 }
|
75
94
|
let(:protocol_version) { 2 }
|
76
95
|
let(:connect_timeout) { 10 }
|
77
96
|
let(:ssl) { false }
|
78
|
-
let(:logger) {
|
97
|
+
let(:logger) { NullLogger.new }
|
79
98
|
let(:compressor) { nil }
|
80
99
|
let(:credentials) { nil }
|
81
100
|
let(:auth_provider) { nil }
|
82
101
|
let(:datacenter) { nil }
|
83
|
-
let(:load_balancing_policy) { LoadBalancing::Policies::TokenAware.new(LoadBalancing::Policies::DCAwareRoundRobin.new(datacenter, 0)) }
|
102
|
+
let(:load_balancing_policy) { LoadBalancing::Policies::TokenAware.new(LoadBalancing::Policies::DCAwareRoundRobin.new(datacenter, 0), shuffle_replicas) }
|
84
103
|
let(:reconnection_policy) { Reconnection::Policies::Exponential.new(0.5, 30, 2) }
|
85
104
|
let(:retry_policy) { Retry::Policies::Default.new }
|
86
105
|
let(:address_resolution_policy) { AddressResolution::Policies::None.new }
|
87
106
|
let(:consistency) { :one }
|
88
107
|
let(:trace) { false }
|
89
|
-
let(:page_size) {
|
108
|
+
let(:page_size) { 10000 }
|
90
109
|
let(:heartbeat_interval) { 30 }
|
91
110
|
let(:idle_timeout) { 60 }
|
92
111
|
let(:timeout) { 10 }
|
112
|
+
let(:synchronize_schema) { true }
|
113
|
+
let(:schema_refresh_delay) { 1 }
|
114
|
+
let(:schema_refresh_timeout) { 10 }
|
115
|
+
let(:thread_pool_size) { 4 }
|
116
|
+
let(:shuffle_replicas) { true }
|
93
117
|
|
94
118
|
let(:connections_per_local_node) { 2 }
|
95
119
|
let(:connections_per_remote_node) { 1 }
|
@@ -97,8 +121,7 @@ module Cassandra
|
|
97
121
|
let(:listeners) { [] }
|
98
122
|
|
99
123
|
def initialize(defaults = {})
|
100
|
-
@
|
101
|
-
@instances = {}
|
124
|
+
@instances = defaults
|
102
125
|
end
|
103
126
|
|
104
127
|
def connect(addresses)
|
@@ -113,7 +136,18 @@ module Cassandra
|
|
113
136
|
addresses.each {|address| cluster_registry.host_found(address)}
|
114
137
|
|
115
138
|
logger.info('Establishing control connection')
|
116
|
-
|
139
|
+
|
140
|
+
promise = futures_factory.promise
|
141
|
+
|
142
|
+
control_connection.connect_async.on_complete do |f|
|
143
|
+
if f.resolved?
|
144
|
+
promise.fulfill(cluster)
|
145
|
+
else
|
146
|
+
f.on_failure {|e| promise.break(e)}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
promise.future
|
117
151
|
end
|
118
152
|
end
|
119
153
|
end
|
data/lib/cassandra/errors.rb
CHANGED
@@ -49,28 +49,28 @@ module Cassandra
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Mixed into all internal driver errors.
|
52
|
-
|
52
|
+
class InternalError < ::RuntimeError
|
53
53
|
include Error, HostError
|
54
54
|
end
|
55
55
|
|
56
56
|
# Raised when data decoding fails.
|
57
57
|
class DecodingError < ::RuntimeError
|
58
|
-
include
|
58
|
+
include Error, HostError
|
59
59
|
end
|
60
60
|
|
61
61
|
# Raised when data encoding fails.
|
62
62
|
class EncodingError < ::RuntimeError
|
63
|
-
include
|
63
|
+
include Error, HostError
|
64
64
|
end
|
65
65
|
|
66
66
|
# Raised when a connection level error occured.
|
67
67
|
class IOError < ::IOError
|
68
|
-
include
|
68
|
+
include Error, HostError
|
69
69
|
end
|
70
70
|
|
71
71
|
# Raised when a timeout has occured.
|
72
72
|
class TimeoutError < ::Timeout::Error
|
73
|
-
include
|
73
|
+
include Error, HostError
|
74
74
|
end
|
75
75
|
|
76
76
|
# Mixed into all request execution errors.
|
@@ -31,6 +31,20 @@ module Cassandra
|
|
31
31
|
# @return [Numeric] request timeout interval
|
32
32
|
attr_reader :timeout
|
33
33
|
|
34
|
+
# @return [String] paging state
|
35
|
+
#
|
36
|
+
# @note Although this feature exists to allow web applications to store
|
37
|
+
# paging state in an [HTTP cookie](http://en.wikipedia.org/wiki/HTTP_cookie), **it is not safe to
|
38
|
+
# expose without encrypting or otherwise securing it**. Paging state
|
39
|
+
# contains information internal to the Apache Cassandra cluster, such as
|
40
|
+
# partition key and data. Additionally, if a paging state is sent with CQL
|
41
|
+
# statement, different from the original, the behavior of Cassandra is
|
42
|
+
# undefined and will likely cause a server process of the coordinator of
|
43
|
+
# such request to abort.
|
44
|
+
#
|
45
|
+
# @see Cassandra::Result#paging_state
|
46
|
+
attr_reader :paging_state
|
47
|
+
|
34
48
|
# @private
|
35
49
|
def initialize(options)
|
36
50
|
consistency = options[:consistency]
|
@@ -38,21 +52,35 @@ module Cassandra
|
|
38
52
|
trace = options[:trace]
|
39
53
|
timeout = options[:timeout]
|
40
54
|
serial_consistency = options[:serial_consistency]
|
55
|
+
paging_state = options[:paging_state]
|
56
|
+
|
57
|
+
Util.assert_one_of(CONSISTENCIES, consistency) { ":consistency must be one of #{CONSISTENCIES.inspect}, #{consistency.inspect} given" }
|
41
58
|
|
42
|
-
|
43
|
-
|
59
|
+
unless serial_consistency.nil?
|
60
|
+
Util.assert_one_of(SERIAL_CONSISTENCIES, serial_consistency) { ":serial_consistency must be one of #{SERIAL_CONSISTENCIES.inspect}, #{serial_consistency.inspect} given" }
|
61
|
+
end
|
44
62
|
|
45
|
-
|
46
|
-
|
63
|
+
unless page_size.nil?
|
64
|
+
page_size = Integer(page_size)
|
65
|
+
Util.assert(page_size > 0) { ":page_size must be a positive integer, #{page_size.inspect} given" }
|
66
|
+
end
|
47
67
|
|
48
|
-
|
49
|
-
|
68
|
+
unless timeout.nil?
|
69
|
+
Util.assert_instance_of(::Numeric, timeout) { ":timeout must be a number of seconds, #{timeout} given" }
|
70
|
+
Util.assert(timeout > 0) { ":timeout must be greater than 0, #{timeout} given" }
|
71
|
+
end
|
72
|
+
|
73
|
+
unless paging_state.nil?
|
74
|
+
paging_state = String(paging_state)
|
75
|
+
Util.assert_not_empty(paging_state) { ":paging_state must not be empty" }
|
76
|
+
end
|
50
77
|
|
51
78
|
@consistency = consistency
|
52
79
|
@page_size = page_size
|
53
80
|
@trace = !!trace
|
54
81
|
@timeout = timeout
|
55
82
|
@serial_consistency = serial_consistency
|
83
|
+
@paging_state = paging_state
|
56
84
|
end
|
57
85
|
|
58
86
|
# @return [Boolean] whether request tracing was enabled
|
@@ -61,8 +89,14 @@ module Cassandra
|
|
61
89
|
end
|
62
90
|
|
63
91
|
# @private
|
64
|
-
def override(options)
|
65
|
-
|
92
|
+
def override(*options)
|
93
|
+
merged = options.unshift(to_h).inject do |base, opts|
|
94
|
+
next base unless opts
|
95
|
+
Util.assert_instance_of(::Hash, opts) { "options must be a Hash, #{options.inspect} given" }
|
96
|
+
base.merge!(opts)
|
97
|
+
end
|
98
|
+
|
99
|
+
Options.new(merged)
|
66
100
|
end
|
67
101
|
|
68
102
|
# @private
|
@@ -108,16 +108,16 @@ module Cassandra
|
|
108
108
|
private
|
109
109
|
|
110
110
|
# @private
|
111
|
-
SELECT_SESSION = "SELECT * FROM system_traces.sessions WHERE session_id =
|
111
|
+
SELECT_SESSION = "SELECT * FROM system_traces.sessions WHERE session_id = %s"
|
112
112
|
# @private
|
113
|
-
SELECT_EVENTS = "SELECT * FROM system_traces.events WHERE session_id =
|
113
|
+
SELECT_EVENTS = "SELECT * FROM system_traces.events WHERE session_id = %s"
|
114
114
|
|
115
115
|
# @private
|
116
116
|
def load
|
117
117
|
synchronize do
|
118
118
|
return if @loaded
|
119
119
|
|
120
|
-
data = @client.query(Statements::Simple.new(SELECT_SESSION
|
120
|
+
data = @client.query(Statements::Simple.new(SELECT_SESSION % @id), VOID_OPTIONS).get.first
|
121
121
|
raise ::RuntimeError, "unable to load trace #{@id}" if data.nil?
|
122
122
|
|
123
123
|
@coordinator = data['coordinator']
|
@@ -138,7 +138,7 @@ module Cassandra
|
|
138
138
|
|
139
139
|
@events = []
|
140
140
|
|
141
|
-
@client.query(Statements::Simple.new(SELECT_EVENTS
|
141
|
+
@client.query(Statements::Simple.new(SELECT_EVENTS % @id), VOID_OPTIONS).get.each do |row|
|
142
142
|
@events << Event.new(row['event_id'], row['activity'], row['source'], row['source_elapsed'], row['thread'])
|
143
143
|
end
|
144
144
|
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2013-2014 DataStax, Inc.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#++
|
18
|
+
|
19
|
+
module Cassandra
|
20
|
+
# @private
|
21
|
+
module Executors
|
22
|
+
class ThreadPool
|
23
|
+
# @private
|
24
|
+
class Task
|
25
|
+
def initialize(*args, &block)
|
26
|
+
@args = args
|
27
|
+
@block = block
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
@block.call(*@args)
|
32
|
+
rescue ::Exception
|
33
|
+
ensure
|
34
|
+
@args = @block = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
include MonitorMixin
|
39
|
+
|
40
|
+
def initialize(size)
|
41
|
+
mon_initialize
|
42
|
+
|
43
|
+
@cond = new_cond
|
44
|
+
@tasks = ::Array.new
|
45
|
+
@waiting = 0
|
46
|
+
@pool = ::Array.new(size, &method(:spawn_thread))
|
47
|
+
@term = false
|
48
|
+
end
|
49
|
+
|
50
|
+
def execute(*args, &block)
|
51
|
+
synchronize do
|
52
|
+
@tasks << Task.new(*args, &block)
|
53
|
+
@cond.signal if @waiting > 0
|
54
|
+
end
|
55
|
+
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def shutdown
|
60
|
+
execute do
|
61
|
+
synchronize do
|
62
|
+
@term = true
|
63
|
+
@cond.broadcast if @waiting > 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def spawn_thread(i)
|
73
|
+
Thread.new(&method(:run))
|
74
|
+
end
|
75
|
+
|
76
|
+
def run
|
77
|
+
Thread.current.abort_on_exception = true
|
78
|
+
|
79
|
+
loop do
|
80
|
+
tasks = nil
|
81
|
+
|
82
|
+
synchronize do
|
83
|
+
@waiting += 1
|
84
|
+
@cond.wait while !@term && @tasks.empty?
|
85
|
+
@waiting -= 1
|
86
|
+
|
87
|
+
return if @tasks.empty?
|
88
|
+
|
89
|
+
tasks = @tasks
|
90
|
+
@tasks = ::Array.new
|
91
|
+
end
|
92
|
+
|
93
|
+
tasks.each(&:run).clear
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class SameThread
|
99
|
+
def execute(*args, &block)
|
100
|
+
yield(*args)
|
101
|
+
nil
|
102
|
+
rescue ::Exception
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def shutdown
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|