Skip to content

Commit 0c17231

Browse files
authored
Fix: properly pass (symbolized) sequel opts (#37)
* [refactor] share driver jars loading logic * [refactor] also share load_driver between plugins * [fix] share and always symbolize sequel opts * need to :nailcare: true/false values as booleans
1 parent 94821e5 commit 0c17231

File tree

8 files changed

+137
-91
lines changed

8 files changed

+137
-91
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 5.0.5
2+
- Fixed user sequel_opts not being passed down properly [#37](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/37)
3+
- Refactored jdbc_streaming to share driver loading, so the fixes from the jdbc plugin also effect jdbc_streaming
4+
15
## 5.0.4
26
- Fixed issue where JDBC Drivers that don't correctly register with Java's DriverManager fail to load (such as Sybase) [#34](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/34)
37

lib/logstash/filters/jdbc_streaming.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# encoding: utf-8
22
require "logstash/filters/base"
33
require "logstash/namespace"
4+
require "logstash/plugin_mixins/jdbc/common"
45
require "logstash/plugin_mixins/jdbc_streaming"
56
require "logstash/plugin_mixins/jdbc_streaming/cache_payload"
67
require "logstash/plugin_mixins/jdbc_streaming/statement_handler"
@@ -46,6 +47,7 @@
4647
# }
4748
#
4849
module LogStash module Filters class JdbcStreaming < LogStash::Filters::Base
50+
include LogStash::PluginMixins::Jdbc::Common
4951
include LogStash::PluginMixins::JdbcStreaming
5052

5153
config_name "jdbc_streaming"

lib/logstash/inputs/jdbc.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# encoding: utf-8
22
require "logstash/inputs/base"
33
require "logstash/namespace"
4+
require "logstash/plugin_mixins/jdbc/common"
45
require "logstash/plugin_mixins/jdbc/jdbc"
56

67
# this require_relative returns early unless the JRuby version is between 9.2.0.0 and 9.2.8.0
@@ -126,6 +127,7 @@
126127
# ---------------------------------------------------------------------------------------------------
127128
#
128129
module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
130+
include LogStash::PluginMixins::Jdbc::Common
129131
include LogStash::PluginMixins::Jdbc::Jdbc
130132
config_name "jdbc"
131133

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
module LogStash module PluginMixins module Jdbc
3+
module Common
4+
5+
private
6+
7+
def complete_sequel_opts(defaults = {})
8+
sequel_opts = @sequel_opts.
9+
map { |key,val| [key.is_a?(String) ? key.to_sym : key, val] }.
10+
map { |key,val| [key, val.eql?('true') ? true : (val.eql?('false') ? false : val)] }
11+
sequel_opts = defaults.merge Hash[sequel_opts]
12+
sequel_opts[:user] = @jdbc_user unless @jdbc_user.nil? || @jdbc_user.empty?
13+
sequel_opts[:password] = @jdbc_password.value unless @jdbc_password.nil?
14+
sequel_opts[:driver] = @driver_impl # Sequel uses this as a fallback, if given URI doesn't auto-load the driver correctly
15+
sequel_opts
16+
end
17+
18+
def load_driver
19+
return @driver_impl if @driver_impl ||= nil
20+
21+
require "java"
22+
require "sequel"
23+
require "sequel/adapters/jdbc"
24+
25+
load_driver_jars
26+
begin
27+
@driver_impl = Sequel::JDBC.load_driver(@jdbc_driver_class)
28+
rescue Sequel::AdapterNotFound => e # Sequel::AdapterNotFound, "#{@jdbc_driver_class} not loaded"
29+
# fix this !!!
30+
message = if jdbc_driver_library_set?
31+
"Are you sure you've included the correct jdbc driver in :jdbc_driver_library?"
32+
else
33+
":jdbc_driver_library is not set, are you sure you included " +
34+
"the proper driver client libraries in your classpath?"
35+
end
36+
raise LogStash::PluginLoadingError, "#{e}. #{message}"
37+
end
38+
end
39+
40+
def load_driver_jars
41+
if jdbc_driver_library_set?
42+
@jdbc_driver_library.split(",").each do |driver_jar|
43+
@logger.debug("loading #{driver_jar}")
44+
# load 'driver.jar' is different than load 'some.rb' as it only causes the file to be added to
45+
# JRuby's class-loader lookup (class) path - won't raise a LoadError when file is not readable
46+
unless FileTest.readable?(driver_jar)
47+
raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, " +
48+
"file not readable (please check user and group permissions for the path)"
49+
end
50+
begin
51+
require driver_jar
52+
rescue LoadError => e
53+
raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e.message}"
54+
rescue StandardError => e
55+
raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e}"
56+
end
57+
end
58+
end
59+
end
60+
61+
def jdbc_driver_library_set?
62+
!@jdbc_driver_library.nil? && !@jdbc_driver_library.empty?
63+
end
64+
65+
end
66+
end end end

lib/logstash/plugin_mixins/jdbc/jdbc.rb

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,12 @@ def setup_jdbc_config
106106

107107
private
108108
def jdbc_connect
109-
opts = {
110-
:user => @jdbc_user,
111-
:password => @jdbc_password.nil? ? nil : @jdbc_password.value,
112-
:pool_timeout => @jdbc_pool_timeout,
113-
:driver => @driver_impl, # Sequel uses this as a fallback, if given URI doesn't auto-load the driver correctly
114-
:keep_reference => false
115-
}.merge(@sequel_opts)
109+
sequel_opts = complete_sequel_opts(:pool_timeout => @jdbc_pool_timeout, :keep_reference => false)
116110
retry_attempts = @connection_retry_attempts
117111
loop do
118112
retry_attempts -= 1
119113
begin
120-
return Sequel.connect(@jdbc_connection_string, opts)
114+
return Sequel.connect(@jdbc_connection_string, sequel_opts)
121115
rescue Sequel::PoolTimeout => e
122116
if retry_attempts <= 0
123117
@logger.error("Failed to connect to database. #{@jdbc_pool_timeout} second timeout exceeded. Tried #{@connection_retry_attempts} times.")
@@ -128,7 +122,7 @@ def jdbc_connect
128122
# rescue Java::JavaSql::SQLException, ::Sequel::Error => e
129123
rescue ::Sequel::Error => e
130124
if retry_attempts <= 0
131-
@logger.error("Unable to connect to database. Tried #{@connection_retry_attempts} times", :error_message => e.message, )
125+
@logger.error("Unable to connect to database. Tried #{@connection_retry_attempts} times", :error_message => e.message)
132126
raise e
133127
else
134128
@logger.error("Unable to connect to database. Trying again", :error_message => e.message)
@@ -138,56 +132,6 @@ def jdbc_connect
138132
end
139133
end
140134

141-
private
142-
143-
def load_driver
144-
if @drivers_loaded.false?
145-
require "java"
146-
require "sequel"
147-
require "sequel/adapters/jdbc"
148-
149-
load_driver_jars
150-
begin
151-
@driver_impl = Sequel::JDBC.load_driver(@jdbc_driver_class)
152-
rescue Sequel::AdapterNotFound => e # Sequel::AdapterNotFound, "#{@jdbc_driver_class} not loaded"
153-
# fix this !!!
154-
message = if jdbc_driver_library_set?
155-
"Are you sure you've included the correct jdbc driver in :jdbc_driver_library?"
156-
else
157-
":jdbc_driver_library is not set, are you sure you included " +
158-
"the proper driver client libraries in your classpath?"
159-
end
160-
raise LogStash::PluginLoadingError, "#{e}. #{message}"
161-
end
162-
@drivers_loaded.make_true
163-
end
164-
end
165-
166-
def load_driver_jars
167-
if jdbc_driver_library_set?
168-
@jdbc_driver_library.split(",").each do |driver_jar|
169-
@logger.debug("loading #{driver_jar}")
170-
# load 'driver.jar' is different than load 'some.rb' as it only causes the file to be added to
171-
# JRuby's class-loader lookup (class) path - won't raise a LoadError when file is not readable
172-
unless FileTest.readable?(driver_jar)
173-
raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, " +
174-
"file not readable (please check user and group permissions for the path)"
175-
end
176-
begin
177-
require driver_jar
178-
rescue LoadError => e
179-
raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e.message}"
180-
rescue StandardError => e
181-
raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e}"
182-
end
183-
end
184-
end
185-
end
186-
187-
def jdbc_driver_library_set?
188-
!@jdbc_driver_library.nil? && !@jdbc_driver_library.empty?
189-
end
190-
191135
def open_jdbc_connection
192136
# at this point driver is already loaded
193137
Sequel.application_timezone = @plugin_timezone.to_sym
@@ -230,7 +174,6 @@ def open_jdbc_connection
230174
public
231175
def prepare_jdbc_connection
232176
@connection_lock = ReentrantLock.new
233-
@drivers_loaded = Concurrent::AtomicBoolean.new
234177
end
235178

236179
public

lib/logstash/plugin_mixins/jdbc_streaming.rb

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -55,37 +55,11 @@ def setup_jdbc_config
5555
config :jdbc_validation_timeout, :validate => :number, :default => 3600
5656
end
5757

58-
private
59-
60-
def load_driver_jars
61-
unless @jdbc_driver_library.nil? || @jdbc_driver_library.empty?
62-
@jdbc_driver_library.split(",").each do |driver_jar|
63-
begin
64-
@logger.debug("loading #{driver_jar}")
65-
# Use https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby#from-jar-files to make classes from jar
66-
# available
67-
require driver_jar
68-
rescue LoadError => e
69-
raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e.message}"
70-
end
71-
end
72-
end
73-
end
74-
7558
public
7659
def prepare_jdbc_connection
77-
require "sequel"
78-
require "sequel/adapters/jdbc"
79-
require "java"
80-
81-
load_driver_jars
82-
83-
@sequel_opts_symbols = @sequel_opts.inject({}) {|hash, (k,v)| hash[k.to_sym] = v; hash}
84-
@sequel_opts_symbols[:user] = @jdbc_user unless @jdbc_user.nil? || @jdbc_user.empty?
85-
@sequel_opts_symbols[:password] = @jdbc_password.value unless @jdbc_password.nil?
60+
load_driver
8661

87-
@sequel_opts_symbols[:driver] = Sequel::JDBC.load_driver(@jdbc_driver_class)
88-
@database = Sequel.connect(@jdbc_connection_string, @sequel_opts_symbols)
62+
@database = Sequel.connect(@jdbc_connection_string, complete_sequel_opts)
8963
if @jdbc_validate_connection
9064
@database.extension(:connection_validator)
9165
@database.pool.connection_validation_timeout = @jdbc_validation_timeout

logstash-integration-jdbc.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |s|
22
s.name = 'logstash-integration-jdbc'
3-
s.version = '5.0.4'
3+
s.version = '5.0.5'
44
s.licenses = ['Apache License (2.0)']
55
s.summary = "Integration with JDBC - input and filter plugins"
66
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"

spec/inputs/jdbc_spec.rb

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
# We do not need to set TZ env var anymore because we can have 'Sequel.application_timezone' set to utc by default now.
1414

1515
describe LogStash::Inputs::Jdbc do
16+
let(:connection_string) { "jdbc:derby:memory:testdb;create=true" }
1617
let(:mixin_settings) do
17-
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
18-
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"}
18+
{
19+
"jdbc_user" => ENV['USER'],
20+
"jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
21+
"jdbc_connection_string" => connection_string
22+
}
1923
end
2024
let(:settings) { {} }
2125
let(:plugin) { LogStash::Inputs::Jdbc.new(mixin_settings.merge(settings)) }
@@ -112,6 +116,57 @@
112116
end
113117
end
114118

119+
context "when sequel opts has user option" do
120+
let(:settings) do
121+
{
122+
"jdbc_user" => 'system',
123+
"statement" => "SELECT * from test_table",
124+
"sequel_opts" => { "user" => 'from-opts' }
125+
}
126+
end
127+
128+
before do
129+
plugin.register
130+
end
131+
132+
after do
133+
plugin.stop
134+
end
135+
136+
it "should honor set jdbc-user when connecting" do
137+
expect( Sequel ).to receive(:connect).with connection_string, hash_including(:user=>"system")
138+
plugin.send(:jdbc_connect)
139+
end
140+
end
141+
142+
context "with sequel opts" do
143+
let(:settings) do
144+
{
145+
"jdbc_user" => 'system',
146+
"statement" => "SELECT * from test_table",
147+
"sequel_opts" => {
148+
"truthy" => 'true',
149+
"falsey" => 'false',
150+
"foo" => 'bar',
151+
"jdbc_properties" => { "some" => 'true' }
152+
}
153+
}
154+
end
155+
156+
before do
157+
plugin.register
158+
end
159+
160+
after do
161+
plugin.stop
162+
end
163+
164+
it "should symbolize keys" do
165+
expect( Sequel ).to receive(:connect).with connection_string,
166+
hash_including(:truthy => true, :falsey => false, :foo => 'bar', :jdbc_properties => { 'some' => 'true' })
167+
plugin.send(:jdbc_connect)
168+
end
169+
end
115170

116171
context "when neither statement and statement_filepath arguments are passed" do
117172
it "should fail to register" do

0 commit comments

Comments
 (0)