Building a maven plugin

Fxlauncher is a auto updating launcher for javafx programs. Download a small jar (around 18Kb), start it and it will download the actual application and run it. Every time you start the small jar it will check if there is a newer version and download it automatically. So fxlauncher is a fun project and it will make keeping a desktop application uptodate easy. Setting up fxlauncher from Maven was not so easy. A plugin was needed.

requirements

The plugin needs to do the following: 1. create the app.xml that is used to describe the application, its dependencies and several settings. 2. update fxlauncher.jar with app.xml 3. copy all the dependencies to a directory from where it can later be uploaded to a webserver 4. upload to a webserver.

All these tasks can be done without writing a plugin for it and was how it was done untill now, this means several calls using the maven-exec-plugin. The setup was huge.

I will not go into details about all of them, but I do want to highlight some interesting things I found while writing this.

Maven plugin documentation is bad

Subject says it all. I did not expect the documentation to be this bad. In the end I analyzed several well known plugins (the dependency plugin, the shade plugin and the jgitflow plugin) to see how a maven-plugin project is supposed to be setup. There is an archetype for it but I could not get that one to work. See pom.xml for how I set it.

java.nio.file.FileSystem is cool

To manipulate the fxlauncher.jar I decided to use the FileSystem that was introduced in Java7. This makes it very easy to add or delete files to, in this case, a jar file.

void addtoLauncher(String fileToAdd) throws MojoExecutionException {
        getLog().info(String.format("placing %s in fxlauncher", fileToAdd));
        Path path = Paths.get(String.format("%s/fxlauncher.jar", buildDir));
        Map<String, String> props = new HashMap<>();
        props.put("create", "false");
        URI uri = URI.create("jar:" + path.toUri().toASCIIString());
        try (FileSystem jarFile = FileSystems.newFileSystem(uri, props)) {
            Path source = Paths.get(fileToAdd);
            Path target = jarFile.getPath(String.valueOf(source.getFileName()));
            Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new MojoExecutionException("error in adding file to jar", e);
        }
    }

The heavy lifting is done in the try block. There are several types of files you can open this way.

mojo-executor is cool too

Normally when you want to copy dependencies to a certain place you will setup the maven-dependency-plugin to do the heavy lifting for you. You would configure it in your pom.xml and tie it to a phase. I wanted however to be able to call the maven-dependency-plugin from another plugin. Luckily this is possible with the mojo-executor:

void copyDependencies() throws MojoExecutionException, IOException {
     getLog().info("copying to " + buildDir);
     executeMojo(
            plugin(groupId("org.apache.maven.plugins"),
                    artifactId("maven-dependency-plugin"),
                    version("2.0")
            ),
            goal("copy-dependencies"),
            configuration(
                    element(name("outputDirectory"), buildDir)
            ),
            executionEnvironment(
                project,
                session,
                buildPluginManager
            ));
}

It doesn’t get much easier then this. I like the fluent like api.