I'm using gradle as the JavaFX plugin. Everything works perfectly even after building and runnig the excecutable at distribution/, except with one class: CloseableHttpClient
For several purposes I create the following object like this:
CloseableHttpClient client = HttpClients.createDefault();
Running the program in the IDE is no problem, everything works fine. But if I build and try to run the .exe-File I get the following Throwable-StackTrace:
java.lang.NoClassDefFoundError: Could not initialize class org.apache.http.conn.ssl.SSLConnectionSocketFactory at org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder.java:955) at org.apache.http.impl.client.HttpClients.createDefault(HttpClients.java:58) at ch.itcb.tools.lom.util.JsonSimpleUtil.http(JsonSimpleUtil.java:29)...
I really don't understand that. How can it be that just this class doesn't get found, but all my other classes do?
My build.gradle file:
apply plugin: 'java' apply plugin: 'eclipse' apply from: 'javafx.plugin' sourceCompatibility = 1.8 version = '0.1' jar { manifest { attributes 'Implementation-Title': 'LogoffManager', 'Implementation-Version': version } } repositories { mavenCentral() } dependencies { compile fileTree(dir: 'lib', include: ['*.jar']) compile 'ch.qos.logback:logback-classic:1.1.3' compile 'org.apache.httpcomponents:httpclient:4.5.1' compile 'com.googlecode.json-simple:json-simple:1.1' compile group: 'commons-collections', name: 'commons-collections', version: '3.2' testCompile group: 'junit', name: 'junit', version: '4.+' } test { systemProperties 'property': 'value' } uploadArchives { repositories { flatDir { dirs 'repos' } } }
Please write a comment if you need more information. Thx.
it's a good question, which I came across just now while researching examples of the many ways Java developers can end up with class path fun :-)
I started with a minimal version of your build.gradle (including only what's directly relevant), specifically:
plugins { id 'java' } sourceCompatibility = 1.8 repositories { mavenCentral() } jar { manifest { attributes 'Main-Class': 'com.oliverlockwood.Main' } } dependencies { compile 'org.apache.httpcomponents:httpclient:4.5.1' }
My 'Main' class, in this context, uses your code example, i.e.:
package com.oliverlockwood; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; public class Main { public static void main(String[] args) { CloseableHttpClient client = HttpClients.createDefault(); } }
At this stage, I can run gradle clean build followed by java -jar build/libs/33106520.jar (my project was named after this StackOverflow question) and I see this:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/impl/client/HttpClients at com.oliverlockwood.Main.main(Main.java:8) Caused by: java.lang.ClassNotFoundException: org.apache.http.impl.client.HttpClients at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
This is subtly different from your error, but before we dig and reproduce that, let me emphasise something: both this error and the one you're seeing are caused at runtime when the classloader is unable to find a class that it needs. There's quite a good blog post here with some more details about the difference between compile-time classpath and runtime classpaths.
If I run gradle dependencies I can see the runtime dependencies for my project:
runtime - Runtime classpath for source set 'main'. \--- org.apache.httpcomponents:httpclient:4.5.1 +--- org.apache.httpcomponents:httpcore:4.4.3 +--- commons-logging:commons-logging:1.2 \--- commons-codec:commons-codec:1.9
I added these manually one-by-one to my runtime classpath. (For the record, this isn't generally considered good practice; but for the sake of the experiment, I copied these jars to my build/libs folder and ran with java -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main. Interestingly enough, this wasn't able to reproduce your exact problem. To recap:
- Without
org.apache.httpcomponents:httpclient available at runtime, then we fail because the HttpClients jar is not found. - With
org.apache.httpcomponents:httpclient:4.5.1 available at runtime, then your problem does not manifest - and I note that the class your build fails to find (org.apache.http.conn.ssl.SSLConnectionSocketFactory) is part of this same Apache library, which is very suspicious indeed.
My suspicion is then that your runtime classpath contains a different version of the Apache httpclient library. Since there's a whole lotta versions out there, I'm not going to test every single combination, so I will instead leave you with the following advice.
- If you want to fully understand the root cause of your issue, then identify exactly which jars (including their versions) are present in your error-case runtime classpath, including any jars that are packaged inside yours if you're creating a fat jar (more on this in point 3). It'd be great if you shared these details here; root cause analysis usually helps everyone to understand better :-)
- Where possible, avoid using dependencies in the manner of
compile fileTree(dir: 'lib', include: ['*.jar']). Managed dependencies based on a repository such as Maven or JCenter are much easier to work with consistently than dependencies in a random directory. If these are internal libraries that you don't want to publish to an open-source artifact repository, then it may be worth setting up a local Nexus instance or similar. - Consider producing a "fat jar" instead of a "thin jar" - this means that all runtime dependencies are packaged in the jar that you build. There's a good Shadow plugin for Gradle that I'd recommend - with this in place in my
build.gradle, and running gradle clean shadow, I was able to run java -jar just fine without needing to manually add anything to my classpath.