2

I'm currently working on a desktop application using Swing, communicating with a server over HTTPS. However, on one production machine which I will have to support, the current development build throws a NoClassDefFoundError despite the class being actually included in the JAR.

The situation is quite simple: I'm starting up the Swing application and configure a server to contact, which will then be used for a quick HTTPS connection test (in the class HTTPClient). I facilitate OkHttp for all communication with the server.

My HTTPClient resembles this code:

package com.myprogram.http;

import okhttp3.*;
import java.net.*;
import java.io.*;

public class HTTPClient {
    private static final OkHttpClient CLIENT = new OkHttpClient ();

    static boolean sendHeartbeat (String server, int port) {
        URL url;
        try { url = new URL ("https", server, port, "/api/v1/heartbeat"); }
        catch (MalformedURLException e) { return false; }

        Request request = new Request.Builder ().url (url).build ();
        try (Response resonse = CLIENT.newCall (request).execute ()) {
            return true;
        } catch (IOException | NullPointerException e) { return false; }
    }
}

This code is being called from middleware which also does state checking etc. and is the real interface between UI and HTTP code. This is the HTTPConnector class:

package com.myprogram.http;

import java.util.concurrent.*;

public class HTTPConnector {
    private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool (10);
    private static String server; // Set by UI code
    private static int port;      // Set by UI code
    
    public static void setServer (String server, int port) {
        HTTPConnector.server = server;
        HTTPConnector.port = port;
    }

    public static void testServerAvailability () {
        if (server == null) throw new IllegalStateException ("Must set server first!");
        
        boolean success = false;
        for (int i = 0; i < 5; i++) {
            Future <Boolean> future = null;
            try {
/* line x */    future = THREAD_POOL.submit (() -> HTTPClient.sendHeartbeat (server, port));
/* line y */    success = future.get (1500, TimeUnit.MILLISECONDS);
                if (success) break;
            } catch (TimeoutException e) {
                e.printStackTrace ();
                System.out.println ("Server status timed out");
                future.cancel (true);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace ();
                System.out.println ("Server status could not be queried");
            }
        }
    }
}

(Please note that I abridged and simplified my code here, for the sake of an MCVE. In reality, I make sure that only one single thread dispatches calls into the HTTPConnector.)

However, upon calling HTTPConnector.testServerAvailability (), I get first a TimeoutException and then four NoClassDefFoundErrors, which read:

java.util.concurrent.TimeoutException
    at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:204)
    at com.myprogram.http.HTTPConnector.testServerAvailability(HTTPConnector.java:x)
    at [REDACTED]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)

java.util.concurrent.ExecutionException: java.lang.NoClassDefFoundError: Could not initialize class com.myprogram.http.HTTPClient
    at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:205)
    at com.myprogram.http.HTTPConnector.testServerAvailability(HTTPConnector.java:y)
    at [REDACTED]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class com.myprogram.http.HTTPClient
    at com.myprogram.http.HTTPConnector.lambda$testServerAvailability$6(HTTPConnector.java:x)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    ... 3 more

I have confirmed that this Lambda is the very first place where any call into the HTTPClient happens, thus is the only place where the class can be initialised.

The only machine I'm seeing this behaviour so far is a Windows 7 Professional (SP1 build 7601) with a Dual-Core 64-bit CPU and 4 GiB RAM. On my Windows 7 Professional (SP1 build 7601) virtual machine with a virtual Dual-Core 64-bit CPU and 10 GiB of RAM, the very same program works. What bothers me is the fact that the JVM uses the very same amount of RAM on both systems. (I'm bundling OpenJDK 17.0.1 next to the JAR, the same executable is used in both cases.)

To me, that would mean that I have a classpath issue but given that the exact same test layout works in my VM, I tend to believe that it is RAM-related. This, however, is even more curious, because of the exact same memory consumption of the JVM.

Upgrading RAM on the production machine is sadly out of discussion, but since I am unsure what actually causes the problems (could it actually be RAM?) I wanted to make sure that I am not doing something blatantly wrong before going to great lengths in searching for an alternative to running the program on this very machine.

In any case, can RAM actually be the culprit here? Or is it CPU clock-speed (3.3 GHz on the production machine vs 3.6 GHz in the VM) and OkHttp needs more than 1500 ms to initialise, hence the TimeoutException when submitting the future?

Alexander Leithner
  • 3,169
  • 22
  • 33
  • As [here](https://stackoverflow.com/q/34413/1426891), note that the presence or absence of the JAR is not relevant as it would be for ClassNotFoundException. Something is happening in the static init of HTTPClient (likely in the instantiation of `CLIENT`, OkHttpClient) that keeps the class def of your HTTPClient from completing its initialization, which would likely be represented as [a `` method](https://stackoverflow.com/q/8517121/1426891). Can you post the logs and look for any exceptions thrown on the affected machine during `com.myprogram.http.HTTPClient.`? – Jeff Bowman Dec 13 '21 at 18:51
  • 1
    @Jeff Sure, will do! I was sure that I had already logged that exception, but that was not the case, as I have noticed while writing this question. I'll come back with the results tomorrow (UTC+1) since I don't currently have access to the machine. – Alexander Leithner Dec 13 '21 at 18:54
  • Thanks. Bear in mind that this might be much further up the logs than your call to `testServerAvailability` and even your TimeoutException, since the first interaction with the HTTPClient class will cause its loading and (presumably) the root cause of the later failures you posted. – Jeff Bowman Dec 13 '21 at 19:03
  • 1
    @Jeff Please have a look at the updated question – a bit of trial and error, however, fixed my problem, I will include an answer shortly. – Alexander Leithner Dec 14 '21 at 11:44

1 Answers1

2

In my case, since I had set a timeout of 1,500 milliseconds on the future and because of the slower CPU clock speed of the misbehaving machine, the class was not fully initialised when the timeout occurred. Turns out, OkHttp is more or less the culprit, it takes more than 5 seconds to inistalise the client on the given machine.

All in all, I am no longer applying any timeout on the first try of the connection test to give OkHttp enough time to initialise itself.

Note that this would not solve the problem if the initialisation of the HTTPClient was to fail at a different point in the application lifecycle. But since the first try of the connection test is the first place to call into HTTPClient, this is the only place where it can be initialised.

Alexander Leithner
  • 3,169
  • 22
  • 33