Java ODBC Data Source ( undefined symbol: SQLAllocEnv ) - java

I have the following Java code. Purpose of this code is to establish a connection
to a remote MySQL database ProductionDb ( a data source defined in my /etc/odbc.ini file ).
import java.sql.*;
import java.util.*;
import java.io.*;
public class Test {
public static void main(String[] args) {
try {
Connection conn = null;
PreparedStatement s = null;
String driver = "sun.jdbc.odbc.JdbcOdbcDriver";
Class.forName(driver).newInstance();
conn = DriverManager.getConnection("jdbc:odbc:ProductionDb");
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
The /etc/odbc.ini file is:
$ cat /etc/odbc.ini
[ProductionDb]
Driver = /usr/lib/odbc/libmyodbc.so
Description = Production Database
Server = [ hidden ]
Port = 3306
User = [ hidden ]
Password = [ hidden ]
Database = ProductionDb
By the way - I am using Java 7 and Ubuntu :
$java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)
$lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 11.04
Release: 11.04
Codename: natty
When I try to run my program I get the following error:
$java Test
java: symbol lookup error: /usr/lib/jvm/java-7-oracle/jre/lib/amd64/libJdbcOdbc.so: undefined symbol: SQLAllocEnv
Does anyone know why I get this error ?
What is wrong here ?
P.S By the way I did run sudo apt-get install unixodbc-dev , sudo apt-get install libmyodbc and sudo apt-get install libmysql-java :-)
UPDATE:
I have also tried the idea suggested in one of the replies below ( by Benny Hill ) : to use the /etc/odbcinst.ini as well as /etc/odbc.ini. Still doesn't work and I get the same error message.
$ cat /etc/odbc.ini
[ProductionDb]
Driver = MySQL Driver
Description = Production Database
Server = [ hidden ]
Port = 3306
User = [ hidden ]
Password = [ hidden ]
Database = ProductionDb
$ cat /etc/odbcinst.ini
[MySQL Driver]
Driver = /usr/lib/odbc/libmyodbc.so
ADDITIONAL NOTE:
I can use this ODBC data source successfully from the R
programming language.
> library(odbc)
> con = odbcConnect("ProductionDb")
> con
RODBC Connection 1
Details:
case=nochange
DSN=ProductionDb

The error is the result of libJdbcOdbc.so looking for the function "SQLAllocEnv" in some other .so and not finding it. The way to debug this is to run the command ldd /usr/lib/jvm/java-7-oracle/jre/lib/amd64/libJdbcOdbc.so. That will show you a list of linked .so objects and where they are located.
Generally speaking they should be in /usr/lib however if you have compiled any software yourself you may find that some of these libs are in /usr/local/lib or some other location. If you have anything that shows up in /usr/local/lib it's possible this is what's causing your problem. To test, rename the library in /usr/local/lib to something else (sudo mv /usr/local/lib/mylib.so /usr/local/lib/mylib.so.SAVE).
Now run your program and see if you still get the same error. If that fixes your problem then great! If not, let us know if you get the same error message or if you get a new one.
I would expect your odbc.ini file to look like this:
[primary]
Description = primary
Driver = iSeries Access ODBC Driver
System = XXX.XXX.XXX.XXX
UserID = XXXXXXXXXX
Password = XXXXXXXXXX
Naming = 0
DefaultLibraries = QGPL
Database = XXXXXXXXXX
ConnectionType = 0
CommitMode = 2
ExtendedDynamic = 0
DefaultPkgLibrary = QGPL
DefaultPackage = A/DEFAULT(IBM),2,0,1,0,512
AllowDataCompression = 1
LibraryView = 0
AllowUnsupportedChar = 0
ForceTranslation = 0
Trace = 0
And your odbcinst.ini file to look like this:
[iSeries Access ODBC Driver]
Description = iSeries Access for Linux ODBC Driver
Driver = /usr/lib/libcwbodbc.so
Setup = /usr/lib/libcwbodbcs.so
NOTE1 = If using unixODBC 2.2.11 or later and you want the 32 and 64-bit ODBC drivers to share DSN's,
NOTE2 = the following Driver64/Setup64 keywords will provide that support.
Driver64 = /usr/lib/lib64/libcwbodbc.so
Setup64 = /usr/lib/lib64/libcwbodbcs.so
Threading = 2
DontDLClose = 1
UsageCount = 1
My example shows my setup for a remote iSeries but I'm sure you can see what you would need to change for MySQL.
Namely your odbc.ini "Driver = ..." line is wrong. It should be something like "Driver = mysql" and then you need to define [mysql] in your odbcinst.ini file.

Sounds like a missing or mismatched library. Try to debug the ldd processing.
First, check what
$ ldd /usr/lib/jvm/java-7-oracle/jre/lib/amd64/libJdbcOdbc.so
says, do all listed dependencies exist?
Then, try setting LD_DEBUG and and start your Java program again, to see the loader debug.
$ export LD_DEBUG=all
$ java Test

To fix this add the following to your startup script or profile:
export LD_PRELOAD=$LD_PRELOAD:/usr/lib/libodbc.so:/usr/lib/libodbcinst.so
Your path may vary a bit, for me the .so files where in /usr/lib64.
"There is a bug due to the fact that the libraries "libodbc.so" and "libodbcinst.so" libJdbcOdbc.so are not loaded from the library that implements the JDBC-ODBC bridge."
According to https://code.google.com/p/queryconnector/source/browse/wiki/HowToInstall.wiki?spec=svn122&r=121

I've used MySQL with JDBC before, and the easiest way I know to connect is using Connector/J which is the official MySQL driver. It will do the same as your ODBC driver, and doesn't require native stuff which may be causing your problem.
Get the driver from here: http://dev.mysql.com/downloads/connector/j/
Docs on above page.
Docs how to connect: http://dev.mysql.com/doc/refman/5.1/en/connector-j-usagenotes-connect-drivermanager.html#connector-j-examples-connection-drivermanager

Go to the directory:
/usr/lib/jvm/java-7-oracle/jre/lib/amd64/
And rename the file libJdbcOdbc.so to libJdbcOdbcXXX.so
This worked for me.

As a workaround and if SQLAllocEnv is defined in /usr/lib/odbc/libmyodbc.so you could try to force loading it before using JDBC
try {
System.load("/usr/lib/odbc/libmyodbc.so");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}

export LD_PRELOAD=$LD_PRELOAD:/usr/lib/libodbc.so:/usr/lib/libodbcinst.so
once I define this in .bash_profile, source it and it works fine for me.

Related

Jenkins SQLServer Choice Parameter - Retrieve data from database

I am trying to get data from SQL Server database table and show it as part of choice parameter as part of a Jenkins Job Build Parameters that I am trying to setup.
I am trying to figure out how to use Extensible Choice for this.
The Choice provider I used is "System Groovy Choice Parameter"
import groovy.sql.Sql
import com.microsoft.sqlserver.jdbc.SQLServerDriver
def output = []
def configuration = [
'dbInstance' : 'servername',
'dbPort' : 0000,
'dbName' : 'dbName',
'dbUser' : 'dbUser',
'dbPass' : 'dbPass'
]
def sql = Sql.newInstance("jdbc:sqlserver://${configuration.dbInstance}:${configuration.dbPort};"
+ "databaseName=" + configuration.dbName,
configuration.dbUser, configuration.dbPass,
'com.microsoft.sqlserver.jdbc.SQLServerDriver')
String sqlString = "SELECT * FROM dbTable"
sql.eachRow(sqlString){ row -> output.push(row[0])
}
return output.sort()
Below is the error I see. Which I understand I see because the jdbc driver is not present. I downloaded the driver from the link below:
https://www.microsoft.com/en-us/download/details.aspx?id=11774
I followed the instructions as to where it should be unzipped to as mentioned in the instructions.
I saw that the CLASSPATH variable is missing, so i went ahead and created the Environment variable with path: "C:\Program Files\sqljdbc_6.0\enu\sqljdbc.jar"
Error: unable to resolve class com.microsoft.sqlserver.jdbc.SQLServerDriver
How do i make sure that the script runs successfully and returns all the data to Extensible Choice. If there is anyother way to do this, I am open to suggestions.
Thank you very much
To resolve the issue I had to copy the "sqljdbc4.jar" file to the following location "C:\Java\jdk1.8.0_92\jre\lib\ext" since that is the path where the JAVA searches for the external jars. Use 4th version for the file which will have 4 in the file name as above as that is version Jenkins supports.

Getting the following error - No suitable driver found for jdbc:postgresql://localhost: 5432/testDBMS

I get back an error indicating java.sql.SQLException: No suitable driver found for
jdbc:postgresql://localhost:
5432/testDBMS
import java.sql.*;
import java.util.*;
public class JdbcPostgresqlConnection {
public static void main(String[] args) {
Connection conn3 = null;
try {
String dbURL3 = "jdbc:postgresql://localhost:5432/testDBMS";
Properties parameters = new Properties();
parameters.put("user", "pgmrHere");
parameters.put("password", "111111");
conn3 = DriverManager.getConnection(dbURL3, parameters);
}
catch (SQLException ex) { ex.printStackTrace(); }
}
}
java -cp . JdbcPostgresqlConnection
So, clearly, the only thing that is in the classpath is the current directory (.). The postgresql driver jar is not. You need to add it to the classpath:
java -cp .:/path/to/driver.jar JdbcPostgresqlConnection
on Linux/MacOS, or
java -cp .;c:\path\to\driver.jar JdbcPostgresqlConnection
on Windows.
The syntax to compile was - java -cp .;"C:\Program Files\PostgreSQL\9.3\lib\postgresql-9.3-1102.jdbc4.jar" JdbcPostgresqlConnection. Note 2 things; the quotes around the jar specification and the jar files cannot be in a folder with a space in the name. This is normally not the case on *nix system, but is often encountered in Windows systems. Note too, that when I put the jar file in the same folder with the java program I could eliminate the double quotes - java -cp .;C:\AZ_Fantasy5\postgresql-9.3-1102.jdbc4.jar JdbcPostgresqlConnection. Special thanks to JB Nizet for pointing out this situation.
As the error says java -cp . JdbcPostgresqlConnection java.sql.SQLException:
No suitable driver found for jdbc:postgresql://localhost: 5432/testDBMS
Which means you didnot include the postgresql.jar in your classpath
Try executing like this and I'm assuming it is Windows OS by seeing your error
java -cp .;pathOfYourDriverjar/postgresql.jar JdbcPostgresqlConnection
Did you not load the driver in your code? Either define the jdbc.drivers property setting it to org.postgresql.Driver or add Class.forName("org.postgresql.Driver") to your code.

Accessing javax.smartcardio from Linux 64 bits

I'm trying to load the smartcard terminals using the javax.smartcardio API with the following code:
public CardTerminal getReadyCardTerminal() throws CardException {
TerminalFactory factory = TerminalFactory.getDefault();
CardTerminals terminals = factory.terminals();
List<CardTerminal> list = terminals.list(State.CARD_PRESENT);
while (list.isEmpty()) {
terminals.waitForChange(1000);
list = terminals.list(State.CARD_PRESENT);
}
CardTerminal cardTerminal = list.get(0);
return cardTerminal;
}
... and I always get the following exception:
java.lang.IllegalStateException: no terminals
at javax.smartcardio.TerminalFactory$NoneCardTerminals.waitForChange(TerminalFactory.java:145)
On Windows Vista/7 everything works fine, but I can't get it to work on Linux. I'm using Ubuntu 12.04 64 bits.
I installed the pcscd service using the following command:
sudo apt-get install libccid pcscd libpcsclite-dev libpcsclite1
sudo service pcscd start
And the pcsc_scan command prints this:
PC/SC device scanner
V 1.4.18 (c) 2001-2011, Ludovic Rousseau <ludovic.rousseau#free.fr>
Compiled with PC/SC lite version: 1.7.4
Using reader plug'n play mechanism
Scanning present readers...
0: OMNIKEY CardMan 3x21 00 00
Tue Sep 11 15:44:49 2012
Reader 0: OMNIKEY CardMan 3x21 00 00
Card state: Card inserted,
ATR: <some hexa codes>
...
So everything looks ok, but the smartcardio just doesn't work. I'm trying with both Oracle and OpenJDK 1.7.0_05, 32 and 64 bits.
The code runs ok with OpenJDK (but not with Oracle JDK, don't know really why) on a Ubuntu 32 bits environment. So I think it is a problem with the 64 bits bridge from Java to the PC/SC library.
Any ideas?
Thanks.
I think I found a workaround for this as I just had a similar problem. In a bugreport from ubuntu it says that the javax.smartcardio library searches for the PC/SC library in the wrong directory.
By specifying the path to the PC/SC library on my machine, like the bugreport mentions, I got it working.
The paths in the bugreport are wrong for me, I'm on 64 bit fedora, where the pc/sc library are installed at /usr/lib64/libpcsclite.so.1
So the workaround for me is to specify the library path to java like this:
java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
Depending on your Linux distribution, the location of libpcsclite.so.1 actually might differ, it could also be at /lib/x86_64-linux-gnu/libpcsclite.so.1 (i.e. Kubuntu 15.04).
In that case, call it like this:
java -Dsun.security.smartcardio.library=/lib/x86_64-linux-gnu/libpcsclite.so.1
i'm using raspberry with debian arm version
find the location of libpcsclite first with:
$ ldd -r /usr/bin/pcsc_scan
and then use the libpcsclite location with:
java -Dsun.security.smartcardio.library=/usr/lib/arm-linux-gnueabihf/libpcsclite.so.1
You need to give the path to the libpcsclite.so.1 when calling your program as follows
java -Dsun.security.smartcardio.library=/path/to/libpcsclite.so.1
If you don't know the path to the library, use the following command
find /usr/lib -name libpcsclite.so.1
This usually shows you the path on your machine. I used it on both Ubuntu 10 (32bit) and Ubuntu 15(32bit and 64bit)
If you're lazy like me, what you can do is include this part of code in your program before you use the javax.smartcardio library
try {
String comm[] = { "find", "/usr", "/lib", "-name",
"libpcsclite.so.1" };
Process p = Runtime.getRuntime().exec(comm);
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((line = reader.readLine()) != null && !line.equals("")) {
if (line.contains("libpcsclite.so.1")) {
System.setProperty("sun.security.smartcardio.library",line);
break;
}
}
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
Now you can run your code from as usual without including the path to libpcsclite.so.1
For anyone else struggling with this on Ubuntu 14 with a 64 bit machine. I found the .so file is actually located in the following directory
/usr/lib/x86_64-linux-gnu/libpcsclite.so
So running my app with the setting as below worked for me
-Dsun.security.smartcardio.library=/usr/lib/x86_64-linux-gnu/libpcsclite.so
Addition to the solution with supplying the path as a parameter like this:
java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
If you don't want to supply this every time you call the JVM, set it in the environment variables _JAVA_OPTIONS and/or JAVA_OPTS:
export _JAVA_OPTIONS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
export JAVA_OPTS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
Since this is a workaround for bug that affects the entire system, it makes sense IMHO to apply this workaround systemwide as well.
JAVA_OPTS has local scope and has to be evaluated by scripts running your code; _JAVA_OPTIONS is supposed to be evaluated automatically by the JRE.
Yet another approach (my favorite) is to make some symbolic links.
It has the advantage that it works system-wide (no jvm arguments, no environment variables).
For my (beloved) debian jessie amd64:
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so libpcsclite.so
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1 libpcsclite.so.1
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1.0.0 libpcsclite.so.1.0.0
Note: This will probably require superuser access.
Complementing #AshanPerera answer, as sometimes searching each time can be slow, you can search it at the first time, and them store the location in a file, and read it from then on:
try {
String filename = "libpcsclite.location";
File propertyFile = new File(filename);
if(propertyFile.createNewFile())
{
String commandWithArguments[] = { "find", "/usr", "/lib", "-name","libpcsclite.so.1" };
Process searchProcess = Runtime.getRuntime().exec(commandWithArguments);
BufferedReader searchReader = new BufferedReader(new InputStreamReader(searchProcess.getInputStream()));
String propertyValue;
while ( (propertyValue = searchReader.readLine()) != null && !propertyValue.equals(""))
{
if (propertyValue.contains("libpcsclite.so.1")) {
BufferedWriter propertyWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(propertyFile)));
propertyWriter.write(propertyValue);
propertyWriter.close();
System.setProperty("sun.security.smartcardio.library",propertyValue);
break;
}
}
searchProcess.waitFor();
}
else
{
BufferedReader propertyReader = new BufferedReader(new InputStreamReader(new FileInputStream(propertyFile)));
String propertyValue = propertyReader.readLine();
System.setProperty("sun.security.smartcardio.library",propertyValue);
}
} catch (Exception e) {
e.printStackTrace();
}

Running a Java program from Adobe AIR's native process

I've use Adobe native process to run java program from my air app. Here the code and it works fine. But i should write absolute path to java runtime for that:
/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java.
If user installed java runtime in diff folder, or have diff version then this code would not work. How i can detect where java were installed or maybe there is another right way to run java applications from air applications? If i run java library from terminal command line then i could just write "java -jar pdfbox-app-1.6.0.jar" etc. and it runs fine.
private function convertPdf2Txt():void{
var arg:Vector.<String> = new Vector.<String>;
arg.push("-jar");
arg.push(File.applicationDirectory.resolvePath("pdfbox-app-1.6.0.jar").nativePath);
arg.push("ExtractText");
arg.push("-force");
arg.push(File.applicationStorageDirectory.resolvePath("Data/1.pdf").nativePath);
arg.push(File.applicationStorageDirectory.resolvePath("Data/1.txt").nativePath);
var fjava:File = new File("/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java");
if (fjava.exists==false){
Alert.show("Can't find Java Runtime in default folder.","Idea Rover",mx.controls.Alert.OK, null,null,imgInfo);
return;
}
var npInfo:NativeProcessStartupInfo;
npInfo = new NativeProcessStartupInfo();
npInfo.executable = fjava;
npInfo.arguments = arg;
var nativeProcess:NativeProcess;
nativeProcess = new NativeProcess();
nativeProcess.addEventListener(NativeProcessExitEvent.EXIT,onNativeProcessExit);
nativeProcess.start(npInfo);
}
Absolute path is:
Mac OS: /usr/bin/java
Win OS: (default)
64bit : C:\Program Files\Java
32bit : C:\Program Files (x86)\Java
rather than popping up an Alert, you could open a file selection dialog, using File.browseForOpen(). then, the File you want is contained in the event passed by the Event.SELECT handler. this flow seems standard for applications i've used that need to access other applications, but aren't sure where to find their executables.
var npInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
// setup npInfo, nativeProcess...
var fjava:File = new File("/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java");
if (!fjava.exists) {
fjava.addEventListener(Event.SELECT, onFileSelected);
fjava.browseForOpen("Where is Java located?");
}
private function onFileSelected (evt:Event) :void {
npInfo.executable = evt.target;
nativeProcess.start(npInfo);
fjava.removeEventListener(Event.SELECT, onFileSelected);
}
of course, you can use the same logic to find the file java needs to launch as well.
You may be able to determine where the Java binaries are by looking at the JAVA_HOME environment variable. I'd like to do the same thing as you're doing, so I'll post more after I do more research.

Java connectivity with PostgreSQL

Can someone please tell me how to connect java file to postgresql database (if possible with code n explanation)
Google is a good start
http://jdbc.postgresql.org/
Here is an example test.java
import java.sql.*;
class test
{
public static void main(String[] args) {
String hostname="", dbname="", username="", password="";
try {
int argno = 0;
hostname = args[argno++];
dbname = args[argno++];
username = args[argno++];
password = args[argno++];
} catch (Exception ex) {
System.err.println("Usage: java -cp driver.jar:. test [hostname] [dbname] [username] [password]");
System.exit(1);
}
try {
Class.forName("org.postgresql.Driver");
Connection connection =
DriverManager.getConnection(
"jdbc:postgresql://"+hostname+"/"+dbname,
username,
password
);
ResultSet rs = connection.createStatement().executeQuery(
"select version() as version"
);
while ( rs.next() ) {
System.out.println(rs.getString("version"));
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Download a current driver from JDBC download page, compile this and run like this on Unices:
java -cp [driver_file_name].jar:. test [hostname] [dbname] [username] [password]
On Windows:
java -cp [driver_file_name].jar;. test [hostname] [dbname] [username] [password]
Just wanted to expound on Tometzky's answer for other beginners using the Netbeans IDE in UNIX like me.
I want the driver to be recognized as a library in the IDE. If you go to Tools->Libraries, you will see the current list. Hit "New Library" and type "PostgreSQL JDBC Driver" or whatever name you want to give it. Then in the Classpath tab, hit "Add JAR/Folder" and point to where you've saved your downloaded driver. I'm not sure if there is a "correct" place to store it, I think it rather depends on how you back up your system and if multiple users share it. Somewhere in your home directory is fine.
After that, make a new project of type "Java Application" and paste Tometzky's code into main. In your project tree, right click on Libraries and add the JDBC driver directly to the project. Now you don't have to worry about specifying the driver on the command line.
Build your project and head over to its "dist" folder. Now you can run it with the command
java -jar myprojectname.jar 127.0.0.1 [dbname] [user] [pw]
That of course assumes you are connect to the database server on your own machine. [user] and [pw] refer to your PostgreSQL username and pw.
Also, when you download the documentation it comes as a bunch of html files. Save them somewhere and point your browser to the index.html file (in Firefox it is File-->Open File).

Categories