Skip to content
Advertisement

JavaFX app overwriting files while being open causes exception

I have a runnable jar file (with a lib folder housing all the dependency jars). This is located on a network share which anyone that has access can run from. This works great except one huge caveat. If I want to deploy a new version of the software, I have to ask everyone to exit the application first. This is because if I overwrite the jars with new versions (or if there is a network blip), the running program stays open but as soon as they do an action that requires code in of the dependencies (jar file in lib folder), it will cause an exception:

Exception in thread "JavaFX Application Thread" java.lang.NoClassDefFoundError

The program will not produce an error, but certain actions will break, like communicating with an API etc.

Is there a way that I can resolve this so that I can publish updates while the user’s are working or at least produce a prompt that will force them to close/and reopen the app etc.

Advertisement

Answer

I was able to create a scheme in which I have multiple server folder locations that house the jar distributable. And this jar basically checks these locations for the latest copy of the application and runs that latest copy. I was able to get it working for both Mac and Windows (didn’t test Linux) by detecting the OS.

So now, I can publish an update over the oldest app, and the next time the user opens the app, it will be the latest copy.

enter image description here

process.properties

location.a=Application/A
location.b=Application/B
app=app.jar

You can add folders A-Z but just add them into the properties.

Main.java

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;


public class Main
{

    public static Properties properties;
    private static final String DEFAULT_PROPERTY_FILE_LOCATION = Paths.get("").toAbsolutePath().toString() + File.separator + "process.properties";
    private static final String JAVE_EXEC;

    static
    {
        String os = System.getProperty("os.name");

        if (StringUtils.containsIgnoreCase(os, "win"))
        {
            JAVA_EXEC = "java";
        } else if (StringUtils.containsIgnoreCase(os, "mac"))
        {
            JAVA_EXEC = "/usr/bin/java";

        } else if (StringUtils.containsIgnoreCase(os, "nux") || StringUtils.containsIgnoreCase(os, "nix"))
        {
            JAVA_EXEC = "/usr/bin/java";
        } else
        {
            JAVA_EXEC = "java";
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {

        Main.properties = new Properties();

        try
        {

            InputStream in = new FileInputStream(DEFAULT_PROPERTY_FILE_LOCATION);
            Main.properties.load(in);
            System.out.println("Loaded property file: " + DEFAULT_PROPERTY_FILE_LOCATION);

            TreeMap<Long, String> locations = new TreeMap<>();
            String appName = Main.properties.getProperty("app");

            if (validateProperties(properties))
            {
                for (int letter = 'a'; letter <= 'z'; ++letter)
                {
                    String location = "location." + (char) letter;
                    if (Main.properties.getProperty(location) != null)
                    {
                        String networkLocation = Paths.get("").toAbsolutePath() + File.separator + Main.properties.getProperty(location);
                        File file = new File(networkLocation + File.separator + appName);
                        if (file.exists())
                        {
                            locations.put(FileUtils.lastModified(file), networkLocation);
                        }
                    }

                }
                if (!locations.isEmpty())
                {
                    Runtime.getRuntime().exec(new String[]
                    {
                        JAVA_EXEC, "-jar", locations.lastEntry().getValue() + File.separator + appName
                    }, null, new File(locations.lastEntry().getValue()));
                }
            }
        } catch (IOException ex)
        {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private static boolean validateProperties(Properties properties)
    {
        List<String> mandatoryProperties = new ArrayList<>();

        mandatoryProperties.add("app");

        for (String mandatoryProperty : mandatoryProperties)
        {
            if (properties.get(mandatoryProperty) == null)
            {
                System.out.println("Failed - Property: " + mandatoryProperty + " doesn't exist.");
                return false;
            }
        }
        return true;

    }

}
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement