web-dev-qa-db-fra.com

NoClassDefFoundError au Runtime avec Gradle

J'utilise gradle comme plugin JavaFX. Tout fonctionne parfaitement même après la construction et l'exécution de l'exécutable à distribution /, sauf avec une classe: CloseableHttpClient

À plusieurs fins, je crée l'objet suivant comme ceci:

CloseableHttpClient client = HttpClients.createDefault();

L'exécution du programme dans le IDE ne pose aucun problème, tout fonctionne bien. Mais si je crée et essaie d'exécuter le fichier .exe, j'obtiens le Throwable- StackTrace suivant:

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)...

Je ne comprends vraiment pas ça. Comment se fait-il que cette classe ne soit pas trouvée, mais toutes mes autres classes le font?

Mon fichier build.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'
       }
    }
}

Veuillez écrire un commentaire si vous avez besoin de plus d'informations. THX.

13
jntme

c'est une bonne question, que j'ai rencontrée tout à l'heure en recherchant des exemples de nombreuses façons Java peuvent se retrouver avec un chemin de classe amusant :-)

J'ai commencé avec une version minimale de votre build.gradle (y compris uniquement ce qui est directement pertinent), en particulier:

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

Dans ce contexte, ma classe `` principale '' utilise votre exemple de code, à savoir:

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

À ce stade, je peux exécuter gradle clean build Suivi de Java -jar build/libs/33106520.jar (Mon projet a été nommé d'après cette question StackOverflow) et je vois ceci:

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)

Ceci est subtilement différent de votre erreur, mais avant de creuser et de reproduire cela, permettez-moi de souligner quelque chose: cette erreur et celle que vous voyez sont causées au moment de l'exécution lorsque le chargeur de classe n'est pas en mesure de trouver une classe dont il a besoin. Il y a un bon article de blog ici avec plus de détails sur la différence entre le chemin de classe au moment de la compilation et les chemins de classe au moment de l'exécution.

Si j'exécute gradle dependencies, Je peux voir les dépendances d'exécution de mon projet:

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

Je les ai ajoutés manuellement un par un à mon chemin de classe d'exécution. (Pour mémoire, ce n'est généralement pas considéré comme une bonne pratique; mais pour le bien de l'expérience, j'ai copié ces pots dans mon dossier build/libs Et j'ai couru avec Java -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main. Fait intéressant, ce n'était pas pas en mesure de reproduire votre problème exact. Pour récapituler:

  • Sans org.Apache.httpcomponents:httpclient Disponible au moment de l'exécution, nous échouons car le fichier HttpClients est introuvable.
  • Avec org.Apache.httpcomponents:httpclient:4.5.1 Disponible au moment de l'exécution, votre problème ne se manifeste pas - et je note que la classe que votre génération ne trouve pas (org.Apache.http.conn.ssl.SSLConnectionSocketFactory) Fait partie de cela même bibliothèque Apache , ce qui est en effet très suspect.

Je soupçonne alors que votre chemin de classe d'exécution contient une version différente de la bibliothèque Apache httpclient. Comme il existe de nombreuses versions, je ne vais pas tester chaque combinaison, je vous laisse donc les conseils suivants.

  1. Si vous voulez bien comprendre la cause première de votre problème, identifiez exactement quels fichiers JAR (y compris leurs versions) sont présents dans votre chemin de classe d'exécution en cas d'erreur , y compris tous les pots emballés dans le vôtre si vous créez un gros pot (plus d'informations à ce sujet au point 3). Ce serait génial si vous partagiez ces détails ici; l'analyse des causes profondes aide généralement tout le monde à mieux comprendre :-)
  2. Dans la mesure du possible, évitez d'utiliser des dépendances à la manière de compile fileTree(dir: 'lib', include: ['*.jar']). Les dépendances gérées basées sur un référentiel tel que Maven ou JCenter sont beaucoup plus faciles à utiliser avec cohérence que les dépendances dans un répertoire aléatoire. S'il s'agit de bibliothèques internes que vous ne souhaitez pas publier dans un référentiel d'artefacts open source, il peut être utile de configurer une instance Nexus locale ou similaire.
  3. Envisagez de produire un "gros pot" au lieu d'un "mince pot" - cela signifie que toutes les dépendances d'exécution sont regroupées dans le pot que vous créez. Il y a un bon plugin Shadow pour Gradle que je recommanderais - avec cela en place dans mon build.gradle, Et en exécutant gradle clean shadow, J'ai pu exécuter Java -jar Très bien sans avoir à ajouter manuellement quoi que ce soit à mon chemin de classe.
14
Olaf