All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zenjava.javafx.maven.plugin.JarMojo Maven / Gradle / Ivy

Go to download

The JavaFX Maven Plugin provides a way to to assemble distributable bundles for JavaFX applications from within Maven. It provides a wrapper around the JavaFX packaging tools which are provided as part of the JavaFX installation.

There is a newer version: 8.8.3
Show newest version
/*
 * Copyright 2012 Daniel Zwolenski.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.zenjava.javafx.maven.plugin;

import com.sun.javafx.tools.packager.CreateJarParams;
import com.sun.javafx.tools.packager.PackagerException;

import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.maven.artifact.Artifact;

/**
 * @goal build-jar
 * @phase package
 * @requiresDependencyResolution
 */
public class JarMojo extends AbstractJfxToolsMojo {

    /**
     * Flag to switch on and off the compiling of CSS files to the binary format. In theory this has some minor
     * performance gains, but it's debatable whether you will notice them, and some people have experienced problems
     * with the resulting compiled files. Use at your own risk. By default this is false and CSS files are left in their
     * plain text format as they are found.
     *
     * @parameter property="jfx.css2bin" default-value=false
     */
    protected boolean css2bin;

    /**
     * A custom class that can act as a Pre-Loader for your app. The Pre-Loader is run before anything else and is
     * useful for showing splash screens or similar 'progress' style windows. For more information on Pre-Loaders, see
     * the official JavaFX packaging documentation.
     *
     * @parameter property="jfx.preLoader"
     */
    protected String preLoader;

    /**
     * Flag to switch on updating the existing jar created with maven. The jar to be updated is taken from
     * '${project.basedir}/target/${project.build.finalName}.jar'.
     * 

* This makes all entries inside MANIFEST.MF being transfered to the jfx-jar. * * @parameter property="jfx.updateExistingJar" default-value=false */ protected boolean updateExistingJar; /** * Set this to true if your app needs to break out of the standard web sandbox and do more powerful functions. *

* If you are using FXML you will need to set this value to true. * * @parameter property="jfx.allPermissions" default-value=false */ protected boolean allPermissions; /** * Will be set when having goal "build-jar" within package-phase and calling "jfx:jar" or "jfx:native" from CLI. Internal usage only. * * @parameter default-value=false */ protected boolean jfxCallFromCLI; /** * To add custom manifest-entries, just add each entry/value-pair here. * * @parameter property="jfx.manifestAttributes" */ protected Map manifestAttributes; /** * For being able to use <userJvmArgs>, we have to copy some dependency when being used. To disable this feature an not having packager.jar * in your project, set this to false. *

* To get more information about, please check the documentation here: https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/jvm_options_api.html. * * @parameter property="jfx.addPackagerJar" default-value=true * @since 8.1.4 */ protected boolean addPackagerJar; /** * In the case you don't want some dependency landing in the generated lib-folder (e.g. complex maven-dependencies), * you now can manually exclude that dependency by added it's coordinates here. * * @parameter property="jfx.classpathExcludes" * @since 8.2.0 */ protected List classpathExcludes = new ArrayList<>(); /** * Per default all listed classpath excludes are ment to be transivie, that means when any direct declared dependency * requires another dependency. *

* When having <classpathExcludes> contains any dependency, that dependency including all transitive dependencies * are filtered while processing lib-files, it's the default behaviour. In the rare case you have some very special setup, * and just want to exlude these dependency, but want to preserve all transitive dependencies going into the lib-folder, * this can be set via this property. *

* Set this to false when you want to have the direct declared dependency excluded from lib-file-processing. * * @parameter property="jfx.classpathExcludesTransient" default-value=true * @since 8.2.0 */ protected boolean classpathExcludesTransient; /** * When you need to add additional files to generated app-folder (e.g. README, license, third-party-tools, ...), * you can specify the source-folder here. All files will be copied recursively. * * @parameter property="jfx.additionalAppResources" */ protected File additionalAppResources; /** * It is possible to copy all files specified by additionalAppResources into the created app-folder containing * your jfx-jar. This makes it possible to have external files (like native binaries) being available while * developing using the run-mojo. * * @parameter property="jfx.copyAdditionalAppResourcesToJar" default-value="false" */ protected boolean copyAdditionalAppResourcesToJar = false; /** * To skip copying all dependencies, set this to true. Please note that all dependencies will be added to the * manifest-classpath as normal, only the copy-process gets skipped. * * @since 8.8.0 * * @parameter property="jfx.skipCopyingDependencies" */ protected boolean skipCopyingDependencies = false; /** * @since 8.8.0 * * @parameter property="jfx.useLibFolderContentForManifestClasspath" default-value="false" */ protected boolean useLibFolderContentForManifestClasspath = false; /** * @since 8.8.0 * * @parameter property="jfx.fixedManifestClasspath" default-value="" */ protected String fixedManifestClasspath = null; @Override public void execute() throws MojoExecutionException, MojoFailureException { if( jfxCallFromCLI ){ getLog().info("call from CLI - skipping creation of JavaFX JAR for application"); return; } if( skip ){ getLog().info("Skipping execution of JarMojo MOJO."); return; } getLog().info("Building JavaFX JAR for application"); Build build = project.getBuild(); CreateJarParams createJarParams = new CreateJarParams(); createJarParams.setOutdir(jfxAppOutputDir); // check if we got some filename ending with ".jar" (found this while checking issue 128) if( !jfxMainAppJarName.toLowerCase().endsWith(".jar") ){ getLog().error("Please provide a proper value for ! It has to end with \".jar\"."); return; } createJarParams.setOutfile(jfxMainAppJarName); createJarParams.setApplicationClass(mainClass); createJarParams.setCss2bin(css2bin); createJarParams.setPreloader(preLoader); if( manifestAttributes == null ){ manifestAttributes = new HashMap<>(); } createJarParams.setManifestAttrs(manifestAttributes); StringBuilder classpath = new StringBuilder(); File libDir = new File(jfxAppOutputDir, libFolderName); if( !libDir.exists() && !libDir.mkdirs() ){ throw new MojoExecutionException("Unable to create app lib dir: " + libDir); } if( updateExistingJar ){ File potentialExistingFile = new File(build.getDirectory() + File.separator + build.getFinalName() + ".jar"); if( !potentialExistingFile.exists() ){ throw new MojoExecutionException("Could not update existing jar-file, because it does not exist. Please make sure this file gets created or exists, or set updateExistingJar to false."); } createJarParams.addResource(null, potentialExistingFile); } else { File potentialExistingGeneratedClasses = new File(build.getOutputDirectory()); // make sure folder exists, it is possible to have just some bootstraping "-jfx.jar" if( !potentialExistingGeneratedClasses.exists() ){ getLog().warn("There were no classes build, this might be a problem of your project, if its not, just ignore this message. Continuing creating JavaFX JAR..."); potentialExistingGeneratedClasses.mkdirs(); } createJarParams.addResource(potentialExistingGeneratedClasses, ""); } try{ if( checkIfJavaIsHavingPackagerJar() ){ getLog().debug("Check if packager.jar needs to be added"); if( addPackagerJar && !skipCopyingDependencies ){ getLog().debug("Searching for packager.jar ..."); String targetPackagerJarPath = libFolderName + File.separator + "packager.jar"; for( Dependency dependency : project.getDependencies() ){ // check only system-scoped if( "system".equalsIgnoreCase(dependency.getScope()) ){ File packagerJarFile = new File(dependency.getSystemPath()); String packagerJarFilePathString = packagerJarFile.toPath().normalize().toString(); if( packagerJarFile.exists() && packagerJarFilePathString.endsWith(targetPackagerJarPath) ){ getLog().debug(String.format("Including packager.jar from system-scope: %s", packagerJarFilePathString)); File dest = new File(libDir, packagerJarFile.getName()); if( !dest.exists() ){ Files.copy(packagerJarFile.toPath(), dest.toPath()); } // this is for INSIDE the manifes-file, so always use "/" classpath.append(libFolderName).append("/").append(packagerJarFile.getName()).append(" "); } } } } else { getLog().debug("No packager.jar will be added"); } } else { if( addPackagerJar ){ getLog().warn("Skipped checking for packager.jar. Please install at least Java 1.8u40 for using this feature."); } } List brokenArtifacts = new ArrayList<>(); project.getArtifacts().stream().filter(artifact -> { // filter all unreadable, non-file artifacts File artifactFile = artifact.getFile(); return artifactFile.isFile() && artifactFile.canRead(); }).filter(artifact -> { if( classpathExcludes.isEmpty() ){ return true; } boolean isListedInList = isListedInExclusionList(artifact); return !isListedInList; }).forEach(artifact -> { File artifactFile = artifact.getFile(); getLog().debug(String.format("Including classpath element: %s", artifactFile.getAbsolutePath())); File dest = new File(libDir, artifactFile.getName()); if( !dest.exists() ){ try{ if( !skipCopyingDependencies ){ Files.copy(artifactFile.toPath(), dest.toPath()); } else { getLog().info(String.format("Skipped copying classpath element: %s", artifactFile.getAbsolutePath())); } } catch(IOException ex){ getLog().warn(String.format("Couldn't read from file %s", artifactFile.getAbsolutePath())); getLog().debug(ex); brokenArtifacts.add(artifactFile.getAbsolutePath()); } } classpath.append(libFolderName).append("/").append(artifactFile.getName()).append(" "); }); if( !brokenArtifacts.isEmpty() ){ throw new MojoExecutionException("Error copying dependencies for application"); } } catch(IOException e){ throw new MojoExecutionException("Error copying dependency for application", e); } if( useLibFolderContentForManifestClasspath ){ StringBuilder scannedClasspath = new StringBuilder(); try{ Files.walkFileTree(libDir.toPath(), new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { scannedClasspath.append(libFolderName.replace("\\", "/")).append("/").append(libDir.toPath().relativize(file).toString().replace("\\", "/")).append(" "); return super.visitFile(file, attrs); } }); } catch(IOException ioex){ getLog().warn("Got problem while scanning lib-folder", ioex); } createJarParams.setClasspath(scannedClasspath.toString()); } else { createJarParams.setClasspath(classpath.toString()); } Optional.ofNullable(fixedManifestClasspath).ifPresent(manifestClasspath -> { if( manifestClasspath.trim().isEmpty() ){ return; } createJarParams.setClasspath(manifestClasspath); if( useLibFolderContentForManifestClasspath ){ getLog().warn("You specified to use the content of the lib-folder AND specified a fixed classpath. The fixed classpath will get taken."); } }); // https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/manifest.html#JSDPG896 if( allPermissions ){ manifestAttributes.put("Permissions", "all-permissions"); } try{ getPackagerLib().packageAsJar(createJarParams); } catch(PackagerException e){ throw new MojoExecutionException("Unable to build JFX JAR for application", e); } if( copyAdditionalAppResourcesToJar ){ Optional.ofNullable(additionalAppResources) .filter(File::exists) .ifPresent(appResources -> { getLog().info("Copying additional app ressources..."); try{ Path targetFolder = jfxAppOutputDir.toPath(); Path sourceFolder = appResources.toPath(); copyRecursive(sourceFolder, targetFolder); } catch(IOException e){ getLog().warn("Couldn't copy additional application resource-file(s).", e); } }); } // cleanup if( libDir.list().length == 0 ){ // remove lib-folder, when nothing ended up there libDir.delete(); } } private boolean checkIfJavaIsHavingPackagerJar() { if( JavaDetectionTools.IS_JAVA_8 && JavaDetectionTools.isAtLeastOracleJavaUpdateVersion(40) ){ return true; } if( JavaDetectionTools.IS_JAVA_9 ){ // NOSONAR return true; } return false; } private boolean isListedInExclusionList(Artifact artifact) { return classpathExcludes.stream().filter(dependency -> { // we are checking for "groupID:artifactId:" because we don't care about versions nor types (jar, war, source, ...) String dependencyTrailIdentifier = dependency.getGroupId() + ":" + dependency.getArtifactId() + ":"; // when not transitive, look at the artifact information if( !classpathExcludesTransient ){ return dependencyTrailIdentifier.startsWith(artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"); } // when transitive, look at the trail return artifact.getDependencyTrail().stream().anyMatch((dependencyTrail) -> (dependencyTrail.startsWith(dependencyTrailIdentifier))); }).count() > 0; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy