When I run my Java code in my IDE, it runs successfully. When I build a jar file and run it from the commandline, I receive a "java.lang.ExceptionInInitializerError" on the line which performs the actual query. I suspect there's a problem with my Apache Ant build script, but I'm not certain.
Here is my extremely simplified code (yes, I know I'm not fully closing out the DB connection on failure, but this is something small I created just to get the simplest version of code I could that still exhibits the error):
package com.company.TestApp.TestProj;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Main {
public Main() {
super();
}
public static void main(String[] args) {
Main main = new Main();
main.run();
}
public void run() {
System.out.println("Starting");
try {
Class.forName("oracle.jdbc.OracleDriver");
Connection db = DriverManager.getConnection(connectionString, schema, password);
if (db.isValid(10)) {
System.out.println("Connected");
}
else {
System.out.println("Not connected");
throw new Exception("Not connected");
}
String sql = "SELECT * FROM dual";
Statement statement = db.createStatement();
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
System.out.println("Record");
}
rs.close();
statement.close();
db.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
My extremely simplified Apache Ant build script is:
<project name="test" basedir="." default="main">
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="main-class" value="com.company.TestApp.TestProj.Main"/>
<property name="lib.dir" value="../lib"/>
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<fileset dir="${jar.dir}">
<include name="**/*.class"/>
</fileset>
<zipfileset src="${lib.dir}/ojdbc7.jar">
<include name="**/*.class"/>
</zipfileset>
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java fork="true" classname="${main-class}">
<classpath>
<path refid="classpath"/>
<path location="${jar.dir}/${ant.project.name}.jar"/>
</classpath>
</java>
</target>
<target name="clean-build" depends="clean,jar"/>
<target name="main" depends="clean,run"/>
</project>
My IDE is JDeveloper 12.1.3.0.0. My JAVA_HOME and path are set to JDK1.8.0_291. My Oracle DB is 19.13.0.0.0
When I run the program from my IDE, I get the expected results of:
Starting
Connected
Record
When I run the jar from the commandline with "java -jar test.jar" the output is:
Starting
Connected
Exception in thread "main" java.lang.ExceptionInInitializerError
at oracle.jdbc.driver.OracleStatement.configureRowData(OracleStatement.java:760)
at oracle.jdbc.driver.OracleStatement.<init>(OracleStatement.java:696)
at oracle.jdbc.driver.T4CStatement.<init>(T4CStatement.java:1152)
at oracle.jdbc.driver.T4CDriverExtension.allocateStatement(T4CDriverExtension.java:46)
at oracle.jdbc.driver.PhysicalConnection.createStatement(PhysicalConnection.java:3914)
at oracle.jdbc.driver.PhysicalConnection.createStatement(PhysicalConnection.java:3874)
at com.company.TestApp.TestProj.Main.run(Main.java:34)
at com.company.TestApp.TestProj.Main.main(Main.java:15)
Caused by: java.lang.NullPointerException
at oracle.jdbc.driver.DynamicByteArray$1.run(DynamicByteArray.java:540)
at java.security.AccessController.doPrivileged(Native Method)
at oracle.jdbc.driver.DynamicByteArray.<clinit>(DynamicByteArray.java:535)
... 8 more
I am running Java JDK1.8.0_291, but I've tried it on other machines with different versions of Java and receive the same error. I'm using the ojdbc7.jar that came with JDeveloper.
To get your fat JAR to run using java -jar test.jar
, you need to make a small change to your Ant script.
Change this:
<zipfileset src="${lib.dir}/ojdbc8-19.3.0.0.jar">
<include name="**/*.class"/>
</zipfileset>
to this:
<zipfileset src="${lib.dir}/ojdbc8-19.3.0.0.jar">
<include name="**/*.*"/>
</zipfileset>
The reason: There are more than just .class
files in the Oracle JDBC JAR.
There are also (for example) properties files such as
defaultConnectionProperties.properties
...and other files.
I don't know which specific missing file was the root cause of your exception. I just performed a build to include everything.
Digression:
You may already know the following - but for future visitors to this question...
It's worth noting also that the JDBC file contains a file called java.sql.Driver
in META-INF/services
- and that file contains the name of the Oracle driver class:
oracle.jdbc.OracleDriver
Normally, this is used to automatically load the Oracle JDBC driver - so you don't need the following line of code:
Class.forName("oracle.jdbc.OracleDriver");
However that works by scanning the library JAR files - and since you no longer have the Oracle JAR file inside your application "fat" jar, you still do need the above line of code.
This is one of those relatively rare cases where that line is still needed, these days, to avoid a No suitable driver found
exception.