NoClassDefFoundError: com/sun/mail/util/MailLogger

Wenn man die Fehlermeldung bekommt

Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/mail/util/MailLogger
        at javax.mail.Session.initLogger(Session.java:283)
        at javax.mail.Session.(Session.java:268)
        at javax.mail.Session.getDefaultInstance(Session.java:378)
        at javax.mail.Session.getDefaultInstance(Session.java:418)
        at eu.masterdevops.sendmail.App.main(App.java:25)
Caused by: java.lang.ClassNotFoundException: com.sun.mail.util.MailLogger
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        … 5 more

dann fehlt vermutlich eine Abhängigkeit in der pom.xml:

<dependency>
  <groupId>com.sun.mail</groupId>
  <artifactId>javax.mail</artifactId>
  <version>1.6.2</version>
</dependency>

Cannot find default setter in class org.apache.maven.archiver.ManifestConfiguration

Beim Bauen eines meiner Java-Projekte bekam ich nach dem Anpassen der pom.xml diese Fehlermeldung

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-assembly-plugin:2.2-beta-5:single
(default-cli) on project <projektname>: Unable to parse configuration of mojo
org.apache.maven.plugins:maven-assembly-plugin:2.2-beta-5:single for parameter manifest:
Cannot find default setter in class org.apache.maven.archiver.ManifestConfiguration -> [Help 1]

Wie es sich herausgestellt hat, hatte ich eine falsche Einstellung in die pom.xml geschrieben (Fehler hier fett abgebildet):

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
    <archive>      <manifest>com.project.projectname.App</manifest>
    </archive>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
  </configuration>
</plugin>

Zwischen <manifest> und </manifest> hätte was anderes rein müssen als die Main-Class:

<archive>
   <manifest>
     <mainClass>com.project.projectname.App </mainClass>
   </manifest>
 </archive>

Nach diese Änderung ist die Fehlermeldung verschwunden.

OpenSSL 1.1: undefined symbol: UINT32_it

Wenn man unter Linux mit dem GNU Compiler eine Bibliothek beim Kompilieren benötigt, kann es doch manchmal wichtig sein, in welcher Reihenfolge man die abhängigen Bibliotheken angibt. Das habe ich letztens wieder gemerkt als mein Programm eine Bibliothek laden wollte, die wiederum die Bibliothek OpenSSL als Abhängigkeit hatte.

Beim Laden der Bibliothek bekam ich diese Fehlermeldung:

undefined symbol: UINT32_it

Nach einer kurzen Suche hatte ich schon eine Antwort. Beim Bauen meiner Bibliothek hatte ich die OpenSSL Bibliotheken in dieser Reihenfolge als abhängige Bibliotheken angegeben:

g++ ... -lcrypto -lssl ...

Nachdem ich die Reihenfolge dieser beiden Bibliotheken beim Bau meiner Bibliothek in

g++ ... -lssl -lcrypto ...

geändert hatte, kam die Fehlermeldung nicht mehr wieder.

Eine statische Bibliothek unter Linux mit CMake erstellen

In diesem Post möchte ich ein kleines Beispiel zeigen, wie man eine statische C++ Bibliothek unter Linux mit Hilfe von CMake baut. Dazu habe ich eine sehr einfache C++-Datei namens funcs.cpp erstellt. Die in der Datei enthaltene Funktion add soll zu Beispielzwecken dann in der statischen Bibliothek enthalten sein.

int add(int a, int b)
{
  return (a+b);
}

Die schon erwähnte Funktion add soll zu Beispielzwecken einfach zwei ganze Zahlen addieren. Um die Datei nun in eine statische Bibliothek zu packen, wird eine weitere Datei namens CMakeLists.txt benötigt, die in etwa so aussieht:

cmake_minimum_required(VERSION 3.6)

# Projektname definieren
project(static_test)

# Liste aller Quelldateien in einer Variablen speichern.
# Aus diesen Dateien entsteht dann die statische Bibliothek
set(ST_SOURCES funcs.cpp)

# CMake sagen, dass eine statische Bibliothek erstellt werden soll.
add_library(static_test STATIC ${ST_SOURCES})

Mit dem Kommando project definiert man den Projektnamen. Mit dem Kommando set wird eine neue Variable namens ST_SOURCES erstellt und gleich definiert. Dabei soll ST_SOURCES aus einer Liste aller Quelltextdateien bestehen, die für die statische Bibliothek nötig sind. Da wir in diesem Beispiel nur eine Datei haben, enthält die Liste ST_SOURCES nur einen Eintrag namens funcs.cpp. Man kann hier aber auch mehrere Dateinamen oder -pfade hintereinander setzen, getrennt durch ein Leerzeichen. Der Variablenname ist frei wählbar, muss aber gültig sein.

Das Kommando add_library sagt CMake nun, dass wir eine Bibliothek bauen wollen. Erst das STATIC in dem Kommando macht CMake klar, dass es sich um eine statische Bibliothek handeln muss. Will man jedoch eine dynamische Bibliothek, ersetzt man das STATIC einfach durch SHARED.

Beide Dateien befinden sich im selben Ordner.

Wenn man nun die CMakeLists.txt erstellt hat, muss man anschließend in die Kommandozeile diese zwei Befehle eingeben:

cmake -G "Unix Makefiles" .
make
Die statische Bibliothek wird gebaut.

Der erste Befehl (cmake …) erstellt nun das entsprechende Makefile. Ist das erstellt, kann man make starten und die statische Bibliothek wird erstellt.

Ist alles erfolgreich verlaufen, müsste man nun im Ordner eine Datei namens libstatic_test.a finden.

Der Bau der statischen Bibliothek war erfolgreich.

‘build.plugins.plugin.version’ for … is missing.

Kürzlich habe ich versucht, ein Projekt mit Maven zu bauen und bekam während des Bauens diese Warnung:

[WARNING]
[WARNING] Some problems were encountered while building the effective model for ::jar:
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-jar-plugin is missing. @ line 17, column 15
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]

Wie es sich herausgestellt hat, hat bei einem verwendeten Maven-Plugin die Versionsangabe gefehlt. In der verwendeten pom.xml wurde das Maven-Plugin maven-jar-plugin verwendet, aber die Version hat gefehlt:

<plugin>
  <artifactId>maven-jar-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <mainClass>com.somepackage.SomeMainClass</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

Die Version für das Plugin konnte ich recht leicht herausfinden und anschließend die pom.xml anpassen:

 <plugin>
  <artifactId>maven-jar-plugin</artifactId>
  <version>3.1.2</version>
  <configuration>
    <archive>
      <manifest>
        <mainClass>com.somepackage.SomeMainClass</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin> 

Danach ist die Warnung verschwunden.

Dependency Graph Viewer Plugin einrichten

Wenn es mal unübersichtlich mit den Jobs in Jenkins wird, kann man mit dem Dependency Graph Viewer Plugin ein wenig Licht in die Sache bringen. Vor allem dann, wenn es darum geht zu sehen, welcher Job welchen anderen Job startet. Wenn z.B. der Job Bauen den Job Unittests aufruft, lässt sich das dem Dependency Graph Viewer Plugin gut visualisieren.

Ein Beispielgraph produziert durch den Dependency Graph Viewer Plugin

Zuerst muss man natürlich das Plugin in Jenkins installieren. Ich gehe jetzt davon aus, dass klar ist wie man in Jenkins ein Plugin installiert.

Als ich nach der Installation versucht hatte, einen Graph zu erstellen, bekam ich stattdessen in der Browser-Konsole eine Fehlermeldung dieser Art. Es gab wohl auch andere, die davon betroffen waren.

TypeError: div.firstChild is null hudson-behavior.js:862:21
    onSuccess http://192.168.0.26:8080/static/b568631b/scripts/hudson-behavior.js:862
    respondToReadyState http://192.168.0.26:8080/static/b568631b/scripts/prototype.js:1657
    onStateChange http://192.168.0.26:8080/static/b568631b/scripts/prototype.js:1600
    bind http://192.168.0.26:8080/static/b568631b/scripts/prototype.js:414

Die Lösung für das Problem besteht aus zwei Teilen:

1. Sicherstellen, dass graphviz aus dem Betriebssystem installiert ist. Mein Jenkins lief auf einem CentOS-System, da reichte es aus graphviz mit dem Befehl

sudo yum install graphviz

zu installieren. Wichtig ist hierbei, dass man nach der Installation das Programm dot finden kann:

sh-4.2# which dot
/usr/bin/dot

2. In den Systemeinstellungen muss der Pfad zu dem Programm dot gesetzt sein.

Hier kann es aber auch sein, dass man auch keinen Pfad setzen muss. Im Zweifel einfach mal testen. Wichtig ist jedoch, dass der Pfad zum dot Programm vollständig ist und hier nicht nur der Pfad gesetzt wird, in dem sich das Programm dot befindet.

Weitere mögliche Quellen:

ERROR: [WS-CLEANUP] Cannot delete workspace: Remote call on … failed

Vor kurzem hatte ich einen Job in Jenkins, der die Einstellung hatte den Arbeitsbereich auf einem Jenkins-Slave zu löschen bevor der Job fortführen sollte:

Auf einmal bekam ich jedoch regelmäßig Fehlermeldungen dieser Art:

[WS-CLEANUP] Deleting project workspace…
[WS-CLEANUP] Deferred wipeout is used…
ERROR: [WS-CLEANUP] Cannot delete workspace: Remote call on … failed
ERROR: Cannot delete workspace: Remote call on … failed

Die Lösung in diesem Fall war die: Ich musste den Jenkins-Slave erst einmal richtig trennen

um ihn anschließend wieder zu verbinden.

Im Grunde habe ich den Slave-Agenten neu gestartet. Seitdem trat der Fehler nicht mehr auf.

Mit docker-compose ein docker Netzwerk erstellen

Mit docker-compose lassen sich mehrere Container auf einmal starten. Man kann damit auch ein docker Netzwerk erstellen. Mit der docker-compose Version 3.5 oder höher kann man auch einen eigenen Namen für das Netzwerk angeben. Hier sieht man eine docker-compose.yml Datei, die zwei Container startet und ein docker Netzwerk erstellt. Den beiden Containern habe ich jetzt die Namen custom1 und custom2 gegeben. Das Netzwerk heißt hier my-own-network-name. Damit beide Container nach dem Start nicht gleich wieder geschlossen werden, läuft ein unendlicher sleep Prozess in ihnen. Für einfache Beispiele sollte diese docker-compose.yml gut genug sein.

version: '3.5'

services:
    custom1:
        container_name: custom1
        image: centos:latest
        entrypoint:
            - sleep
            - inf
        networks:
            - my-own-network-name
    custom2:
        container_name: custom2
        image: centos:latest
        entrypoint:
            - sleep
            - inf
        networks:
            - my-own-network-name
networks:
    my-own-network-name:
         name: my-own-network-name

Wenn man nun also die Kommandos:

docker-compose up -d
docker network ls

ausführt, sieht man das neu erstellte docker Netzwerk:

Um die erstellten Container und das docker Netzwerk zu löschen, reicht das Kommando

docker-compose down

Caused by: java.lang.NoClassDefFoundError: com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider

Ich hatte kürzlich Jenkins frisch aufgesetzt und die empfohlenen Plugins installiert. Als ich ein Multibranch Pipeline Projekt erstellt hatte und konfigurieren wollte, ging nichts mehr. Ich konnte nichts konfigurieren und bekam zudem diese Fehlermeldung zu Gesicht:

org.apache.commons.jelly.JellyTagException: jar:file:/root/.jenkins/war/WEB-INF/lib/jenkins-core-2.176-SNAPSHOT.jar!/lib/hudson/actions.jelly:39:70: <st:include> com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider at org.apache.commons.jelly.impl.TagScript.handleException(TagScript.java:745) at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:289) at org.apache.commons.jelly.TagSupport.invokeBody(TagSupport.java:161) at org.apache.commons.jelly.tags.core.ForEachTag.doTag(ForEachTag.java:150) at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:269)
...
Caused by: java.lang.NoClassDefFoundError: com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider at java.lang.Class.getDeclaringClass0(Native Method) at java.lang.Class.getDeclaringClass(Class.java:1235) at java.lang.Class.getEnclosingClass(Class.java:1277) at com.cloudbees.plugins.credentials.CredentialsStore.<init>(CredentialsStore.java:102)
...
Caused: javax.servlet.ServletException at org.kohsuke.stapler.jelly.JellyClassTearOff.serveIndexJelly(JellyClassTearOff.java:119) at org.kohsuke.stapler.jelly.JellyFacet.handleIndexRequest(JellyFacet.java:146) at org.kohsuke.stapler.IndexViewDispatcher.dispatch(IndexViewDispatcher.java:30) at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:747)

Die wichtige Fehlermeldung ist hier fett hervorgehoben. Ursache für dieses Problem ist, dass ein Plugin nicht installiert gewesen ist: Das Cloudbees Credentials Plugin.

Ein Visual Studio 2019 Projekt mit CMake erstellen

Seit der Version 3.14 kann man mit CMake Visual Studio 2019 Projekte erstellen. In diesem Post möchte ich kurz zeigen, wie das möglich ist.

Um dieses Beispiel erfolgreich zum Laufen zu bringen, müssen einige Bedingungen erfüllt sein:

  • Visual Studio 2019 muss auf dem Rechner installiert sein.
  • CMake 3.14 (oder höher) muss installiert sein
  • Der Pfad zur cmake.exe muss in der PATH Variable enthalten sein

Mehr braucht es auch nicht. Nun will ich aber mit meinem Beispiel fortfahren. Mein Beispielprojekt für diesen Post besteht nur aus einer einzigen C++-Datei. Das Programm am Ende soll einfach ein Hallo, Welt! in der Kommandozeile ausgeben:

include <iostream>
int main(int argc, char **argv)
{
std::cout << "Hallo, Welt!" << std::endl;
return 0;
}

Wenn man Projekte mit CMake erstellt, gehört dazu eine Datei mit dem Namen CMakeLists.txt. Für dieses Beispiel sieht die so aus:

project(example1)

set(SOURCE_FIlES main1.cpp)

add_executable(example1 ${SOURCE_FIlES})

In der CMakeLists.txt definiere ich in der ersten Zeile den Projektnamen. Diese Zeile sorgt später dafür, dass am Ende eine Solutionsdatei namens example1.sln existiert. Diese Solutionsdatei werden wir später nutzen, um mit Hilfe von Visual Studio 2019 aus dem Code eine ausführbare Datei zu produzieren.

Als nächstes werden alle nötigen Codedateien in der Variablen SOURCE_FILES aufgelistet. Für komplexere Projekte gibt es andere Methoden, aber für unser Beispiel hier ist das ausreichend.

In der letzten Zeile sagen wir dann, dass wir aus den Codedateien eine ausführbare Datei namens example1 erstellt haben wollen (add_executable). Da wir hier unter Windows arbeiten und Visual Studio 2019 nutzen, brauchen wir die Endung hier nicht zu schreiben.

Sowohl die CMakeLists.txt wie auch die C++-Datei sollten in diesem Beispiel in einem Ordner nebeneinander liegen:

CMakeLists.txt und main1.cpp liegen in einem Ordner

Da wir das Projekt nachher noch mit Visual Studio 2019 bauen wollen, rufen wir die x64 Native Tools Command Prompt for VS 2019 aus dem Startmenü auf:

Nun müsste ein Fenster wie das Folgende auftauchen:

In der gestarteten Kommandozile müssen wir nun in den Ordner wechseln, in dem sich die CMakeLists.txt befindet. Sind wir dort, rufen wir CMake mit folgendem Kommando auf:

cmake -G "Visual Studio 16 2019" .

Der Punkt am Ende steht gewollt da. Ist alles gut gegangen, sehen wir am Ende diese Meldung (der Pfad kann sich hierbei anders sein):

-- Build files have been written to: C:/Users/jenkins/Documents/cmake1

Ist alles erfolgreich verlaufen, müsste man in dem Ordner eine Datei namens example1.sln finden. Daneben auch eine example1.vcxproj. Diese Solutionsdatei kann man nun in Visual Studio öffnen oder anderweitig bearbeiten. Jetzt können wir anfangen, das Projekt in der Kommandozeile zu bauen, z.B. durch einen Befehl wie diesen:

msbuild example1.sln