NoClassDefFoundError во время выполнения с Gradle

я использую gradle в качестве плагина JavaFX. Все работает отлично даже после строительства и runnig excecutable at распределение/, за исключением одного класса: CloseableHttpClient

для нескольких целей я создаю следующий объект вроде этого:

CloseableHttpClient client = HttpClients.createDefault();

запуск программы в IDE не проблема, все работает нормально. Но если я построю и попытаюсь запустить .EXE-файл, я получаю следующее 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)...

Я действительно не понимать это. Как может быть, что только этот класс не найден,но все мои другие классы?

моя сборка.файл gradle:

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'
       }
    }
}

пожалуйста, напишите комментарий, если вам нужна дополнительная информация. Тнх.

1 ответов


это хороший вопрос, с которым я столкнулся только сейчас, исследуя примеры многих способов разработчиков Java, которые могут закончиться классом path fun: -)

я начал с минимальной версии вашей сборки.gradle (включая только то, что имеет непосредственное отношение), в частности:

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'
}

мой "основной" класс в этом контексте использует ваш пример кода, т. е.:

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();
    }
}

на этом этапе я могу бежать gradle clean build следовал по java -jar build/libs/33106520.jar (мой проект был назван в честь этого StackOverflow вопрос), и я вижу это:

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)

это тонко отличается от вашей ошибки, но прежде чем копать и воспроизводить это, позвольте мне подчеркнуть кое-что: и эта ошибка, и та, которую вы видите, вызваны во время выполнения, когда загрузчик классов не может найти класс, который ему нужен. Там довольно хороший пост в блоге здесь С некоторыми более подробными сведениями о разнице между classpath во время компиляции и runtime classpaths.

если я запускаю gradle dependencies I показаны зависимости времени выполнения для моего проекта:

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

я добавил их вручную один за другим в свой путь к классам среды выполнения. (Для справки, это обычно не считается хорошей практикой; но ради эксперимента я скопировал эти банки в свой build/libs папка и побежал с java -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main. Интересно, что это не смогло воспроизвести вашу точную проблему. Напомним:

  • без org.apache.httpcomponents:httpclient доступно во время выполнения, то мы терпим неудачу, потому что HttpClients jar не найдено.
  • С org.apache.httpcomponents:httpclient:4.5.1 доступно во время выполнения, тогда ваша проблема не проявляется - и я отмечаю, что класс, который ваша сборка не может найти (org.apache.http.conn.ssl.SSLConnectionSocketFactory) составляет часть этой же библиотеки Apache, что действительно очень подозрительно.

я подозреваю, что ваш путь к классам среды выполнения содержит разные версии библиотеки Apache httpclient. Поскольку существует множество версий, я не собираюсь тестировать каждую из них комбинация, поэтому я вместо этого оставлю вам следующий совет.

  1. если вы хотите полностью понять основную причину вашей проблемы, то определите ровно какие банки (включая их версии) присутствуют в вашем пути к классам времени выполнения ошибок, включая любые банки, которые упакованы внутри вашего, если вы создаете жирную банку (подробнее об этом в пункте 3). Было бы здорово, если бы вы поделились этими деталями здесь; анализ первопричин обычно помогает всем понять лучше :-)
  2. по возможности избегайте использования зависимостей в виде compile fileTree(dir: 'lib', include: ['*.jar']). Управляемые зависимости, основанные на репозитории, таком как Maven или JCenter, намного проще работать последовательно, чем зависимости в случайном каталоге. Если это внутренние библиотеки, которые вы не хотите публиковать в репозитории артефактов с открытым исходным кодом, то, возможно, стоит настроить локальный экземпляр Nexus или аналогичный.
  3. рассмотрите возможность производства "жирной банки" вместо " тонкой банки" - это означает, что все зависимости времени выполнения упакованы в jar, который вы строите. Есть хороший теневой плагин для Gradle что я бы рекомендовал - с этим на месте в моем build.gradle и под управлением gradle clean shadow, я смог запустить java -jar просто отлично, без необходимости вручную добавлять что-либо в мой путь к классам.