Why isn't jruby closing db connections? - java

My calls to Oracle via jruby aren't closing their db connections.
Here is the code from the webpage making the call:
<%
require 'jdbc_ssl_connection'
# Database settings
url = "jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(HOST=**REMOVED**)(PORT=**REMOVED**))(CONNECT_DATA=(SERVICE_NAME=**REMOVED**)))"
output = ""
select_stmt, rest, select_sql = nil
begin
conn = OracleConnection.create(url)
# Display connection using the to_s method of OracleConnection
select_sql = "select FIELD from SCHEMA.TABLE WHERE FIELD='"+#subject["file-name"].first+"'"
select_stmt = conn.create_statement
rset = select_stmt.execute_query select_sql
while (rset.next)
output = output + rset.getString(1)
end
rescue
error = "Error:", $!, "\n"
ensure
if (!select_stmt.nil?)
select_stmt.close
end
if (!rset.nil?)
rset.close
end
if (!conn.nil?)
conn.close_connection
end
end
%>
Here is the class that interacts with the driver.
# jdbc_ssl_connection.rb
require 'java'
java_import 'oracle.jdbc.OracleDriver'
java_import 'java.sql.DriverManager'
java_import 'java.util.Properties'
class OracleConnection
#conn = nil
def initialize (url)
#url = url
properties = java.util.Properties.new
properties['user'] = 'REMOVED'
properties['password'] = 'REMOVED'
# Load driver class
oradriver = OracleDriver.new
DriverManager.registerDriver oradriver
#conn = DriverManager.get_connection url, properties
#conn.auto_commit = false
end
# Add getters and setters for all attributes we wish to expose
attr_reader :url, :connection
def close_connection()
#conn.close() unless #conn
end
def prepare_call(call)
#conn.prepare_call call
end
def create_statement()
#conn.create_statement
end
def prepare_statement(sql)
#conn.prepare_statement sql
end
def commit()
#conn.commit
end
def self.create(url)
conn = new(url)
end
def to_s
"OracleConnection [url=#{#url}]"
end
alias_method :to_string, :to_s
end
The code works and is pretty simple. I ran a test and I have about 100 open sessions on the db. For some reason the call to close the connection isn't stopping the session. Any ideas what might be wrong?

def close_connection()
#conn.close() unless #conn
end
because of the conditional, you really wanted: #conn.close if #conn

Related

Athena JDBC Driver (v42_2.0.31): Calling .getSchema() returns empty schema, must use internal methods?

I've noticed that even when setting a JDBC URL that specifies the Schema property as the manual says, if you call .getSchema() on the Connection, it's null:
Class.forName("com.simba.athena.jdbc42.Driver")
val athenaDs = com.simba.athena.jdbc42.DataSource()
val region = "us-east-1"
val catalog = "AwsDataCatalog"
val schema = "chinook"
val user = "access_key"
val password = "secret_key"
val s3Output = "s3://my-bucket/my-folder"
val jdbcUrl = "jdbc:awsathena://AwsRegion=$region;Catalog=$catalog;Schema=$schema;User=$user;Password=$password;S3OutputLocation=$s3Output"
athenaDs.setURL(jdbcUrl)
val conn = athenaDs.connection
val s42Connection = conn.unwrap(com.simba.athena.jdbc.jdbc42.S42Connection::class.java)
val schemaSetting = com.simba.athena.support.SettingReader.readSetting("SCHEMA")
val supportsSchema = com.simba.athena.dsi.core.utilities.PropertyUtilities.hasSchemaSupport(s42Connection.connection)
println(
"""
Catalog: ${conn.catalog}
Schema (conn.schema): ${conn.schema}
Schema (SettingReader): $schemaSetting
PropertyUtilities.hasSchemaSupport(conn): $supportsSchema
"""
)
Output:
Catalog: AwsDataCatalog
Schema (conn.schema):
Schema (SettingReader): chinook
PropertyUtilities.hasSchemaSupport(conn): true
I've decompiled the driver using Recaf and looked at the source, that was how I found out about the hack to use SettingReader.readSetting("SCHEMA") to get the schema
But this doesn't feel right and I'm wondering:
Why doesn't Connection#getSchema() work
Is there a cleaner way to do this?

Passing additional parameters to dbConnect function for JDBCDriver in R

I am trying to connect to HiveServer2 via JDBC drivers from R using RJDBC package. I have seen a broad explanation on passing additional arguments to dbConnect wrapper for various drivers(What arguments can I pass to dbConnect?), but there appear that situation with JDBCDriver is a bit tricker than for other drivers. I can connect to HiveServer2 under this specific URL adress url = paste0("jdbc:hive2://", host = 'tools-1.hadoop.srv', ":", port = 10000, "/loghost;auth=noSasl") . The correspoding code works and enables me to write statements on Hive from R
library(RJDBC)
dbConnect(drv = JDBC(driverClass = "org.apache.hive.jdbc.HiveDriver",
classPath = c("/opt/hive/lib/hive-jdbc-1.0.0-standalone.jar",
"/usr/share/hadoop/share/hadoop/common/lib/commons-configuration-1.6.jar",
"/usr/share/hadoop/share/hadoop/common/hadoop-common-2.4.1.jar"),
identifier.quote = "`"), # to juz niekoniecznie jest potrzebne
url = paste0("jdbc:hive2://", host = 'tools-1.hadoop.srv', ":", port = 10000, "/loghost;auth=noSasl"),
username = "mkosinski") -> conn
I am wondering if there is a way to pass arguments such as database name (loghost) or a no_authentication_mode (auth=noSasl) to ... in dbConnect such that I could only specify standard URL address (url = paste0("jdbc:hive2://", host = 'tools-1.hadoop.srv', ":", port = 10000)) and somehow pass the rest of parametrs like this
library(RJDBC)
dbConnect(drv = JDBC(driverClass = "org.apache.hive.jdbc.HiveDriver",
classPath = c("/opt/hive/lib/hive-jdbc-1.0.0-standalone.jar",
"/usr/share/hadoop/share/hadoop/common/lib/commons-configuration-1.6.jar",
"/usr/share/hadoop/share/hadoop/common/hadoop-common-2.4.1.jar"),
identifier.quote = "`"), # to juz niekoniecznie jest potrzebne
url = paste0("jdbc:hive2://", host = 'tools-1.hadoop.srv', ":", port = 10000),
username = "mkosinski", dbname = "loghost", auth = "noSasl") -> conn
But the second approach doesn't look to work, despite the various combinations of names and values of additional arguments I try.
Does anyone know how to pass additional arguments to DBI::dbConnect through ... parameter for JDBCDriver?
According to the author's answer: https://github.com/s-u/RJDBC/issues/31#issuecomment-173934951
Simply anything - all that dbConnect does is to collect whatever you
pass (including ...) and collect it all into a property dictionary
(java.util.Properties) that is passed to the driver's connect()
method. So any named argument you pass is included. So the only
special argument is url which is passed directly, everything else is
included in the properties. How that gets interpreted is out of
RJDBC's hands - it's entirely up to the driver.
there you can use the full url
library(RJDBC)
drv <- JDBC("org.postgresql.Driver","C:/R/postgresql-9.4.1211.jar")
con <- dbConnect(drv, url="jdbc:postgresql://host:port/dbname", user="<user name>", password="<password>")

can not instantiate driver class in Meteor method

I am trying to use JDBC inside my meteor application but I was not successful in loading the driver class.
the code is simple enough:
console.log("testing jdbc " + process.env.PWD + "/server/ojdbc14-11.2.jar");
var java = Meteor.require('java');
java.classpath.push(process.env.PWD + '/server/ojdbc14-11.2.jar');
console.log("class loaded");
var driver = java.newInstanceSync('oracle.jdbc.driver.OracleDriver');
console.log("driver instantiated");
var result = java.callStaticMethodSync('java.sql.DriverManager','registerDriver', driver);
console.log("driver registered");
result = java.callStaticMethodSync('java.sql.DriverManager','getConnection', 'jdbc:oracle:thin:user/password#local.sertal.ch:7788/KND1');
console.log("connection established");
I am using the Meteor npm package to include java the console never shows the message driver instantiated
I tried the same thing with a naked nodejs script and it worked fine. The code is almost the same:
var java = require('java');
java.classpath.push(__dirname + '/ojdbc14-11.2.jar');
var driver = java.newInstanceSync('oracle.jdbc.driver.OracleDriver');
var result = java.callStaticMethodSync('java.sql.DriverManager','registerDriver', driver);
var connection = java.callStaticMethodSync('java.sql.DriverManager', 'getConnection', 'jdbc:oracle:thin:user/password#local.sertal.ch:7788/KND1');

Connection cannot be cast to oracle.jdbc.OracleConnection

Why java.sql.Connection cannot be cast to oracle.jdbc.OracleConnection in code below?
My main goal is to pass to Oracle connection new user name and save it in 'SESSION' table in for example 'osuser' column because I want to trace in DB user changes and display it in the table.
#Repository
public class AuditLogDAOImpl implements AuditLogDAO {
#PersistenceContext(unitName="myUnitName")
EntityManager em;
#Resource(name = "dataSource")
DataSource dataSource;
public void init() {
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
OracleConnection oracleConnection = (OracleConnection) connection; //Here I got cast exception!
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = "my_new_username";
oracleConnection.setEndToEndMetrics(metrics, (short) 0);
java.util.Properties props = new java.util.Properties();
props.put("osuser", "newValue");
oracleConnection.setClientInfo(props);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Here is error log:
10:42:29,251 INFO [STDOUT] org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6#bcc8cb
10:42:51,701 ERROR [STDERR] java.lang.ClassCastException: $Proxy286 cannot be cast to oracle.jdbc.OracleConnection
Generally I have 2 problem in this case:
why cast from Connection to OracleConnection fails and
what is the best way to implement my intend (I mean set the new user name to v$session.osuser in Oracle DB?
I work with Oracle 11g, Hibernate (using entity manager), data source via jndi.
Please help, thanks!
EDIT:
After some improvement the problem with casting still exists.
Improvement:
Connection connection = DataSourceUtils.getConnection(dataSource);
connection = ((org.jboss.resource.adapter.jdbc.WrappedConnection)connection).getUnderlyingConnection();
OracleConnection oracleConnection = (OracleConnection) connection;
Error:
java.lang.ClassCastException: $Proxy287 cannot be cast to org.jboss.resource.adapter.jdbc.WrappedConnection
The connection you are retrieving is probably a wrapped connection.
If you really need to get the underlying Oracle connection you should use:
if (connection.isWrapperFor(OracleConnection.class)){
OracleConnection oracleConnection= connection.unwrap(OracleConnection.class);
}else{
// recover, not an oracle connection
}
The isWrapperFor and unwrap methods are available since Java 1.6, and should be meaningfully implemented by the A/S connection wrappers.
The connection pool usually has a wrapper around the real connection instance, that's why your cast fails.
What you are doing wouldn't work anyway, because the parameters in the properties instance are only checked when the connection is established. As you have a connection that is already active, it won't change anything.
You need tou use DBMS_APPLICATION_INFO.SET_CLIENT_INFO() in order to change this for an existing connection.
This is just for people who come here via search on how to set metrics in OracleConnection, I spend great deal of time on this, so might help someone.
After you get your "connection" this should work:
DatabaseMetaData dmd = connection.getMetaData();
Connection metaDataConnection = null;
if(dmd != null)
{
metaDataConnection = dmd.getConnection();
}
if(!(metaDataConnection instanceof OracleConnection))
{
log.error("Connection is not instance of OracleConnection, returning");
return; /* Not connection u want */
}
OracleConnection oraConnection = (OracleConnection)metaDataConnection;
String[] metrics = new String[END_TO_END_STATE_INDEX_MAX]; // Do the rest below...
It works for me for OracleConnection, but I face diff issue when setting metrics:
short zero = 0;
oraConnection.setEndToEndMetrics(metrics, zero);
After proxying connection via my method where I set metrics few times, I get:
java.sql.SQLRecoverableException: No more data to read from socket
But I think it has to do with some Spring wiring inits or connection pool.
i had faced this issue when using spring to get connections. Typically , each layer adds a wrapper over the basic classes. i had just done connection.getClass().getName() to see the runtime type of the connection being retuned. It will be a Wrapper/proxy over which you can easily find the method to get the base OracleConnection type.
Try the following
I had encountered the same issue. We were using spring and it has a class called
NativeJdbcExtractor. It has many implementations and the following one works for TomCat. There is a specific implementation for Jboss called the JBossNativeJdbcExtractor
<bean id="jdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"></bean>
In your DAO you can inject the bean and use the following method
protected NativeJdbcExtractor jdbcExtractor;
Connection conn=jdbcExtractor.getNativeConnection(oracleConnection);
You can access the inner OracleObject inside a Wrapper, in this case the wrapper
type is NewProxyConnection:
( I've used it in my project, it Worked ...no mistery, just use reflection)
Field[] fieldsConn= connection.getClass().getDeclaredFields();
Object innerConnObject = getFieldByName(fieldsConn,"inner").get(connection);
if(innerConnObject instanceof OracleConnection ){
OracleConnection oracleConn = (OracleConnection)innerConnObject;
//OracleConnection unwrap = ((OracleConnection)innerConnObject).unwrap();
// now you have the OracleObject that the Wrapper
}
//Method: Set properties of the ooject accessible.
public static Field getFieldByName(Field[] campos, String name) {
Field f = null;
for (Field campo : campos) {
campo.setAccessible(true);
if (campo.getName().equals(name)) {
f = campo;
break;
}
}
return f;
}
Not sure if my situation is related, but with my project, simply changing a database configuration setting actually causes unwrap to fail!
I'm using the Play framework with Scala; this works for me only when logSql=false:
db.withConnection { implicit c =>
val oracleConnection = c.unwrap(classOf[OracleConnection])
}
(this is just the Scala version of unwrapping an OracleConnection)
When I set logSql=true, I get:
com.sun.proxy.$Proxy17 cannot be cast to oracle.jdbc.OracleConnection
java.lang.ClassCastException: com.sun.proxy.$Proxy17 cannot be cast to
oracle.jdbc.OracleConnection
So something about the logSql configuration can actually cause unwrap to fail. No idea why.
With either configuration, my connection object is:
HikariProxyConnection#1880261898 wrapping
oracle.jdbc.driver.T4CConnection#6b28f065
isWrapperFor(OracleConnection) is true in both cases
This happens with Hikari Connection Pool and Bone CP. Maybe it's a bug in Oracle JDBC?
Oracle JDBC Driver version according to MANIFEST.MF
Implementation-Version: 11.2.0.3.0
Repository-Id: JAVAVM_11.2.0.4.0_LINUX.X64_130711
After trial and error.
This way works:
DelegatingConnection delConnection = new DelegatingConnection(dbcpConnection);
oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();
But this way returned a null pointer for oraConnection:
DelegatingConnection delConnection = (DelegatingConnection) dbcpConnection;
oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();
The following worked to get around AQ's TopicConnection.getTopicSession => JMS-112
//DEBUG: Native DataSource : weblogic.jdbc.common.internal.RmiDataSource
con = DataSource.getConnection();
debug("Generic SQL Connection: " + con.toString());
//DEBUG: Generic Connection: weblogic.jdbc.wrapper.PoolConnection_oracle_jdbc_driver_T4CConnection
if (con != null && con.isWrapperFor(OracleConnection.class)) {
WebLogicNativeJdbcExtractor wlne = new WebLogicNativeJdbcExtractor();//org.springframework to the rescue!!
java.sql.Connection nativeCon = wlne.getNativeConnection(con);
this.oraConnection = (OracleConnection) nativeCon;
debug("Unwrapp SQL Connection: " + this.oraConnection.toString());
}
//DEBUG: Native Connection: oracle.jdbc.driver.T4CConnection รจ
Now I could use this in the AQ-Factory w/o JMS-112
try casting like below
WrappedConnectionJDK6 wc = (WrappedConnectionJDK6) connection;
connection = wc.getUnderlyingConnection();

Loading and connecting to mysql jdbc driver runtime

I am currently working in a requirement where I need to load the mysql driver runtime and connect to the database using java.
I am using URLClassLoader to load the jar file
File f = new File("D:/Pallavi/workspace/WarInstallation/mysql-connector-java-5.0.4-bin.jar"); //Jar path
URLClassLoader urlCl = new URLClassLoader(new URL[] { f.toURL()},System.class.getClassLoader());
Class sqldriver = urlCl.loadClass("com.mysql.jdbc.Driver"); // Runtime loading
Driver ds = (Driver) sqldriver.newInstance(); //Compilation failing as "sqldriver" class of type Driver is not found
//I am using now java.sql.Driver to remove the compilation error
sqldriver = Class.forName("com.mysql.jdbc.Driver", true, sqldriver.getClassLoader()).newInstance(); //Runtime fail.. "Driver" Class not Found Exception.
Although the class loads fine I can't establish a Database connection (No suitable driver found for ...) no matter which driver I try.
Please suggest a way to load the jdbc "com.mysql.jdbc.Driver" class runtime.
Let me know, if you need any further information, as this is urgent.
Thanks in advance.
I have three questions before I answer to your issues:
Statement 1:
ya, I have set the classpath of mysql jar in the environment variables, do we need to set it through system properties?
Q1: Why are to relying on custom class loader, when a class is readily available to the System class loader from the class path?
You don't need explicit class path to mysql***.jar to use custom class loader.
Statement 2:
Class sqldriver = urlCl.loadClass("com.mysql.jdbc.Driver"); // Runtime loading
//Compilation failing as "sqldriver" class of type Driver is not found
Driver ds = (Driver) sqldriver.newInstance();
Q2: Claiming Compilation failing ... is very conflicting. Is your compiler looking for such class to generate your class!?
I am sure it is not. May be the error is at run time with a java.lang.ClassNotFoundException: com.mysql.jdbc.Driver. And I also suspect the comment should go with your Statement 3 below.
If it is a CNFE, your file path to mysql***.jar is wrong. Fix it first.
Statement 3:
//I am using now java.sql.Driver to remove the compilation error
//Runtime fail.. "Driver" Class not Found Exception.
sqldriver = Class.forName("com.mysql.jdbc.Driver", true, sqldriver.getClassLoader()).newInstance();
Q3: Claiming ... "Driver" Class not Found Exception is suspectful. Because, this statement won't get compiled. Then how can it be a Runtime fail. ..!?
I also suspect the comment should go with your Statement 2 above.
And here, you need to call newInstance() and then cast to java.sql.Driver before assigning to sqlDriver variable. Because Class.forName( ... only returns a Class object associated with the class or interface with the given string name.
If issue at Statement 2 above is fixed, you can apply this fix to test further.
Let me hope you got these statements clarified.
I have a working sample code below, with a tested output shown for you.
import java.io.File; // and others as required
public class MySQLDriveClassLoader {
public static void main( String [] args ) throws Exception {
//File f = new File( "/home/ravinder/soft-dump/mysql-connector-java-5.1.18-bin.jar" );
File f = new File( "E:\\Soft_Dump\\mysql-connector-java-5.0.4\\mysql-connector-java-5.0.4-bin.jar" );
URLClassLoader urlCl = new URLClassLoader( new URL[] { f.toURI().toURL() }, System.class.getClassLoader() );
Class mySqlDriver = urlCl.loadClass( "com.mysql.jdbc.Driver" );
//*** Start: DEBUG *************************
//mySqlDriver.con // On pressing CTRL+SPACEBAR, after .con, IDE shows "No default proposals"
// meaning it still is not an instance of Driver, and hence can't call a method from Driver class.
//Incompatible conditional operand types Class and Driver
//System.out.println( mySqlDriver instanceof java.sql.Driver ) );
System.out.println( "mySqlDriver: " + mySqlDriver );
System.out.println( "Is this interface? = " + mySqlDriver.isInterface() );
Class interfaces[] = mySqlDriver.getInterfaces();
int i = 1;
for( Class _interface : interfaces ) {
System.out.println( "Implemented Interface Name " + ( i++ ) + " = " + _interface.getName() );
} // for(...)
Constructor constructors[] = mySqlDriver.getConstructors();
for( Constructor constructor : constructors ) {
System.out.println( "Constructor Name = " + constructor.getName() );
System.out.println( "Is Constructor Accessible? = " + constructor.isAccessible() );
} // for(...)
//*** End : DEBUG *************************
Driver sqlDriverInstance = ( Driver ) mySqlDriver.newInstance();
System.out.println( "sqlDriverInstance: " + sqlDriverInstance );
Connection con = null;
try {
/******************************************************************
// You may fail to register the above driver
// hence don't depend on DriverManager to get Connected
//DriverManager.registerDriver( sqlDriverInstance );
//Driver driver = DriverManager.getDriver( "com.mysql.jdbc.Driver" ); // ( "jdbc:mysql" );
Enumeration<Driver> enumDrivers = DriverManager.getDrivers();
while ( enumDrivers.hasMoreElements() ) {
Driver driver = enumDrivers.nextElement();
System.out.println( "driver: " + driver );
} // while drivers
//******************************************************************/
String dbUrl = "jdbc:mysql://:3306/test";
Properties userDbCredentials = new Properties();
userDbCredentials.put( "user", "root" );
userDbCredentials.put( "password", "password" );
// No suitable driver found for ...
//con = DriverManager.getConnection( dbUrl, "root", "password" );
// safely use driver to connect
con = sqlDriverInstance.connect( dbUrl, userDbCredentials );
System.out.println( "con: " + con );
Statement stmt = con.createStatement();
String sql = "select now()";
ResultSet rs = stmt.executeQuery( sql );
if ( rs.next() ) {
System.out.println( rs.getString( 1 ) );
} // if rs
} catch( Exception e ) {
e.printStackTrace(); // only for quick debug
} finally {
try { if ( con != null ) con.close(); } catch ( Exception ignoreThis ) {}
}
} // psvm(...)
} // class MySQLDriveClassLoader
A successful compilation and run, resulted following output:
mySqlDriver: class com.mysql.jdbc.Driver
Is this interface? = false
Implemented Interface Name 1 = java.sql.Driver
Constructor Name = com.mysql.jdbc.Driver
Is Constructor Accessible? = false
sqlDriverInstance: com.mysql.jdbc.Driver#1270b73
con: com.mysql.jdbc.Connection#32fb4f
2012-05-29 03:52:12.0
DriverManager ignores classes loaded at runtime, it will work only for classes loaded by the System class loader.
You can create a Dummy driver class which encapsulates your actual database driver. Source code can be found here.
Off topic:
File.toURL is deprecated, instead get URL from File using toURL on URI
URLClassLoader urlCl = new URLClassLoader(new URL[] { f.toURI().toURL()},System.class.getClassLoader());

Categories

Resources