require 'jruby' module LogStash module PluginMixins module Jdbc module Common private # NOTE: using the JRuby mechanism to load classes (through JavaSupport) # makes the lock redundant although it does not hurt to have it around. DRIVERS_LOADING_LOCK = java.util.concurrent.locks.ReentrantLock.new() def complete_sequel_opts(defaults = {}) sequel_opts = @sequel_opts. map { |key,val| [key.is_a?(String) ? key.to_sym : key, val] }. map { |key,val| [key, val.eql?('true') ? true : (val.eql?('false') ? false : val)] } sequel_opts = defaults.merge Hash[sequel_opts] sequel_opts[:user] = @jdbc_user unless @jdbc_user.nil? || @jdbc_user.empty? sequel_opts[:password] = @jdbc_password.value unless @jdbc_password.nil? sequel_opts[:driver] = @driver_impl # Sequel uses this as a fallback, if given URI doesn't auto-load the driver correctly sequel_opts end def load_driver return @driver_impl if @driver_impl ||= nil require "java" require "sequel" require "sequel/adapters/jdbc" # execute all the driver loading related duties in a serial fashion to avoid # concurrency related problems with multiple pipelines and multiple drivers DRIVERS_LOADING_LOCK.lock() begin load_driver_jars begin @driver_impl = load_jdbc_driver_class rescue => e # catch java.lang.ClassNotFoundException, potential errors # (e.g. ExceptionInInitializerError or LinkageError) won't get caught message = if jdbc_driver_library_set? "Are you sure you've included the correct jdbc driver in :jdbc_driver_library?" else ":jdbc_driver_library is not set, are you sure you included " + "the proper driver client libraries in your classpath?" end raise LogStash::PluginLoadingError, "#{e.inspect}. #{message}" end ensure DRIVERS_LOADING_LOCK.unlock() end end def load_driver_jars if jdbc_driver_library_set? @jdbc_driver_library.split(",").each do |driver_jar| @logger.debug("loading #{driver_jar}") # load 'driver.jar' is different than load 'some.rb' as it only causes the file to be added to # JRuby's class-loader lookup (class) path - won't raise a LoadError when file is not readable unless FileTest.readable?(driver_jar) raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, " + "file not readable (please check user and group permissions for the path)" end begin require driver_jar rescue LoadError => e raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e.message}" rescue StandardError => e raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e}" end end end end def jdbc_driver_library_set? !@jdbc_driver_library.nil? && !@jdbc_driver_library.empty? end def load_jdbc_driver_class # sub a potential: 'Java::org::my.Driver' to 'org.my.Driver' klass = @jdbc_driver_class.gsub('::', '.').sub(/^Java\./, '') # NOTE: JRuby's Java::JavaClass.for_name which considers the custom class-loader(s) # in 9.3 the API changed and thus to avoid surprises we go down to the Java API : klass = JRuby.runtime.getJavaSupport.loadJavaClass(klass) # throws ClassNotFoundException # unfortunately we can not simply return the wrapped java.lang.Class instance as # Sequel assumes to be able to do a `driver_class.new` which only works on the proxy, org.jruby.javasupport.Java.getProxyClass(JRuby.runtime, klass) end end end end end