JavaFX 11 javafx.scene.image constructed from URL is throwing SSLHandshakeException when application is run from built JRT module

Cladgeman :

I am developing a javaFX 11 application that uses the javafx.scene.image class to load images from a URL:

Image​(String url, boolean backgroundLoading)

where backgroundLoading is set to true.

The application works fine when run from my Eclipse IDE (using Maven). But when the application is built as a modular (JRT) application and I run the launcher to test the build, my Image objects do not load from their assigned URLs (https protocol) and instead indicate an error. The exception returned is:

java.util.concurrent.ExecutionException: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

I have tried switching backgroundLoading to false and tried URLs from other sites that provide access to images. I have even tried changing the protocol property of the URLs from "https" to "http". Still the same outcome.

I have an ImageHandler class for handling the construction and setting of javafx.scene.image objects and another class that uses the ImageHandler to set the image of a javafx.scene.image.ImageView object (by using a listener to check when the background loading has finished).

I cannot provide the full code to reproduce the problem, but here are some snippets from the aforementioned classes:

ImageHandler - getImage():

public Image getImage() {
    if (this.image == null || this.imageHadErrorLoading()) {
        this.imageUrl = String.format(Settings.GATHERER_URL + ImageHandler.QUERY_DATA, this.multiverseId);
        LoggerUtil.logger(this).log(Level.INFO, String.format("URL for image: %s", this.imageUrl));
        try {
            this.image = new Image(this.imageUrl, this.backgroundLoading);
            this.setImageError(false);  
        } catch (Exception e) {
            LoggerUtil.logger(this).log(Level.SEVERE, String.format("Exception creating new Image: %s", e.toString()));
        }
    }

    return this.image;
}

ViewController - setCurrentImage():

private void setCurrentImage(int multiverseId) {
    ImageHandler imageHandler;
    imageHandler = new ImageHandler(multiverseId, true);
    Image cardImage = imageHandler.getImage();

    // If the image was previously loaded (successfully), just set the image.
    // Otherwise, use a listener to monitor the loading of the image which
    // eventually sets the image once it has successfully loaded.
    if (imageHandler.imageHasLoaded()) {
        this.cardImageView.setImage(cardImage);
        LoggerUtil.logger(this).log(Level.INFO, String.format("Multiverse ID %d: Image cached....getting image....", multiverseId));
    } else {
        // This listener on the image's progress is used to set the Image View when the image has finally loaded. In the meantime,
        // the Image View will continue to display the "placeholder" image.
        cardImage.progressProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                double cardProgress = (double) newValue;
                if (cardProgress == 1.0) {
                    if (cardImage.isError()) {
                        cardImageView.setImage(imageHandler.getErrorImage());
                        LoggerUtil.logger(this).log(Level.SEVERE, String.format("Multiverse ID %d: Error loading image.", multiverseId));
                        LoggerUtil.logger(this).log(Level.SEVERE, String.format("Image exception was: %s", cardImage.getException().getMessage()));
                        LoggerUtil.logger(this).log(Level.SEVERE, String.format("Image exception was: %s", cardImage.getException()));
                    } else {
                        cardImageView.setImage(cardImage);
                        imageHandler.setImageLoaded(true);
                        LoggerUtil.logger(this).log(Level.INFO, String.format("Multiverse ID %d: Image loaded successfully! Image URL: %s", multiverseId, cardImage.getUrl()));
                    }
                }
            }
        });
    }

Expected outcome: Application displays images when run from a built JRT module and has the same behaviour as if run from the Eclipse IDE.

Actual outcome: Application images produce SSLHandshakeException when run from a built JRT module

Cladgeman :

Thanks to @jewelsea I found out that I needed to add jdk.crypto.ec as a module option when building the application using the javafx-maven-plugin. See this answer for the details.

It worked OK when using OpenJDK 11 in Eclipse and running from source, but certain modules such as jdk.crypto.ec must get omitted by default when building a JRT image through jlink, unless specifically added.

Here is my updated javafx-maven-plugin definition in my Maven POM:

<plugin>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-maven-plugin</artifactId>
    <version>0.0.2</version>
    <configuration>
        <release>11</release>
        <jlinkImageName>thecollector</jlinkImageName>
        <launcher>launcher</launcher>
        <mainClass>thecollector/com.cladge.thecollector.MainApp</mainClass>
        <options>
            <option>--add-modules</option>
            <option>jdk.crypto.ec</option>
        </options>
    </configuration>
</plugin>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=148389&siteId=1