ClassNotFoundException upon running JAR, no errors while running in IntelliJ IDEA
Asked Answered
C

3

20

I'm just starting to build Java apps (I do have .NET experience) and I was trying to build a small test app whose whole code is this :

package com.company;

import com.microsoft.sqlserver.jdbc.SQLServerDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Main {

    public static void main(String[] args) throws SQLException {
        System.out.println("Buna lume!");

        SQLServerDataSource ds = new SQLServerDataSource();
        ds.setIntegratedSecurity(true);
        ds.setServerName("localhost");
        ds.setPortNumber(1433);
        ds.setDatabaseName("Test");
        Connection con = ds.getConnection();

        String SQL = "SELECT * FROM Test WHERE ID = ?";
        PreparedStatement stmt = con.prepareStatement(SQL);
        stmt.setInt(1, 2);
        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getInt(1) + ", " + rs.getString(2));
        }
        rs.close();
        stmt.close();

        con.close();
    }
}

If I run the app in the IDE (IntelliJ IDEA 12.1.6 Community Edition), having the SQL Server available, the app runs fine doing exactly what it should.

The SQL Server Driver is downloaded from Microsoft and added as an External Library. I have created an artifact as JAR file :

enter image description here

Now, if I include the sqljdbc4.jar in the cliTest.jar (my JAR) the resulted JAR is fat (550kB and includes the classes in that JAR too). Or I can exclude it. Either way, running

java -jar cliTest.jar

Results in

Buna lume!
Exception in thread "main" java.lang.NoClassDefFoundError: com/microsoft/sqlserv
er/jdbc/SQLServerDataSource
        at com.company.Main.main(Main.java:15)
Caused by: java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLSer
verDataSource
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 1 more

I bet I'm missing something quite basic but I just can't figure out what exactly is causing this.

LE1 : I tried adding sqljdbc4.jar (although it didn't seem necessary) and sqljdbc_auth.dll in the directory containing the JAR but still no change.

Later edit 2 : Based on Nikolay's answer I've done the following :

  1. Deleted the existing artifact
  2. Created a new artifact like so :

enter image description here

enter image description here

.. and resulted this :

enter image description here

  1. Build -> Build artifacts -> cliTest.jar -> Rebuild

  2. CMD Prompt at the folder containing :

[cliTest.jar 566 KB was generated]

java -jar cliTest.jar

Now I get :

Exception in thread "main" java.lang.SecurityException: Invalid signature file d
igest for Manifest main attributes
    at sun.security.util.SignatureFileVerifier.processImpl(Unknown Source)
    at sun.security.util.SignatureFileVerifier.process(Unknown Source)
    at java.util.jar.JarVerifier.processEntry(Unknown Source)
    at java.util.jar.JarVerifier.update(Unknown Source)
    at java.util.jar.JarFile.initializeVerifier(Unknown Source)
    at java.util.jar.JarFile.getInputStream(Unknown Source)
    at sun.misc.URLClassPath$JarLoader$2.getInputStream(Unknown Source)
    at sun.misc.Resource.cachedInputStream(Unknown Source)
    at sun.misc.Resource.getByteBuffer(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$100(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Cordle answered 3/11, 2013 at 19:55 Comment(0)
C
6

Well I should have built the JAR without the sqljdbc4.jar embedded.

The second thing I should have run the command like so :

java -classpath sqljdbc4.jar;cliTest.jar com.company.Main

.. and then all worked!

Cordle answered 3/11, 2013 at 21:3 Comment(3)
This is one way. But embedding every class file in a single jar should also work. Note that jar files are actually zip files, so you can always open them and look inside to see whether they contain all the classes.Alfrediaalfredo
Yes, I did peek in the dependent JAR (sqljdbc4.jar) but trying to emved it into my resulted JAR would yield the SecurityException as in the last part of the question. I guess the JAR that was being embedded was signed and the encompassing one not ... or something like that.Undervest
Yes, if you embed a all files of a signed jar in a big jar, the signature can cause problems. You can remove the META-INF/*.SF, META-INF/*.DSA and META-INF/*.RSA files to get rid of the signature.Alfrediaalfredo
B
7

Use java -cp instead of java -jar and put all of you dependencies jars to classpath.

Another way is pack all of dependencies to single jar, that allowing you to run application using java -jar.

EDIT:

In Java *.jar file contains a bulk of classes. When you build your own app, typically, result jar file contains only your classes, but still have to load classes from external libraries you use (so-called dependencies).

It can be done two different ways:

  1. You create a folder for your application, for example, called lib and place your application jar and all dependencies into. Then you run application using java -cp lib:/\* com.company.Main or (thanks @NilsH, I miss this variant) you make MANIFEST.MF file and specify Main-Class and Classpath attributes inside as described here

  2. You use special tool (like maven-dependency-plugin if you use maven for build) to pack all classes, either your own, either external to single jar. You got one huge file and can run it using java -jar cliTest.jar.

Generally, first approach is preferred and using a MANIFEST.MF file is a good form.

Buttery answered 3/11, 2013 at 19:58 Comment(6)
Ok, but I still build the JAR or not?Undervest
Yes, I've explained my answer.Buttery
I have to disagree. The "proper" way to build an executable jar is to have a proper MANIFEST.MF file in the META-INF folder of the jar file and run it with java -jar. The MANIFEST.MF file should contain entries for the dependent jars in a Class-Path section, in addition to the Main-Class attribute of the manifest.Clark
Not in all cases. Some big projects using java -cp way. For example, Apache Tomcat starts with java -classpath, but JBoss AS7 use java -jar. PS: I added this suggestion to answer.Buttery
Added later edit... the java -cp lib:/* com.company.Main results me a dull Error: Could not find or load main class com.company.MainUndervest
After later edit you can do java -jar because you selected "extract to target jar" option. This error means, as I guess, that sqljdbc.jar is signed. Related question is #999989 . Try to follow NilsH suggestion by selecting "copy to output directory and link via manifest".Buttery
C
6

Well I should have built the JAR without the sqljdbc4.jar embedded.

The second thing I should have run the command like so :

java -classpath sqljdbc4.jar;cliTest.jar com.company.Main

.. and then all worked!

Cordle answered 3/11, 2013 at 21:3 Comment(3)
This is one way. But embedding every class file in a single jar should also work. Note that jar files are actually zip files, so you can always open them and look inside to see whether they contain all the classes.Alfrediaalfredo
Yes, I did peek in the dependent JAR (sqljdbc4.jar) but trying to emved it into my resulted JAR would yield the SecurityException as in the last part of the question. I guess the JAR that was being embedded was signed and the encompassing one not ... or something like that.Undervest
Yes, if you embed a all files of a signed jar in a big jar, the signature can cause problems. You can remove the META-INF/*.SF, META-INF/*.DSA and META-INF/*.RSA files to get rid of the signature.Alfrediaalfredo
U
0

I made the following change to build.gradle, where <packageName> is package <packageName>.

This appears at the top of your file Main.kt or Main.java

  manifest {
-    attributes "Main-Class": "MainKt"
+    attributes "Main-Class": "<packageName>.MainKt"
  }
  from {
    configurations.runtimeClasspath.collect {
      f -> f.isDirectory() ? f : zipTree(f)
    }
  }
  archiveName "pipeline-server.jar"
}

Unstoppable answered 21/1, 2021 at 19:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.